跳转至

核心代码

alloc.c

/* This file is concerned with allocating and freeing arbitrary-size blocks of
 * physical memory on behalf of the FORK and EXEC system calls.  The key data
 * structure used is the hole table, which maintains a list of holes in memory.
 * It is kept sorted in order of increasing memory address. The addresses
 * it contains refers to physical memory, starting at absolute address 0
 * (i.e., they are not relative to the start of PM).  During system
 * initialization, that part of memory containing the interrupt vectors,
 * kernel, and PM are "allocated" to mark them as not available and to
 * remove them from the hole list.
 *
 * The entry points into this file are:
 *   alloc_mem: allocate a given sized chunk of memory
 *   free_mem:  release a previously allocated chunk of memory
 *   mem_init:  initialize the tables when PM start up
 *   max_hole:  returns the largest hole currently available
 *   mem_holes_copy: for outsiders who want a copy of the hole-list
 */

#include "pm.h"
#include <minix/com.h>
#include <minix/callnr.h>
#include <minix/type.h>
#include <minix/config.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include "mproc.h"
#include "../../kernel/const.h"
#include "../../kernel/config.h"
#include "../../kernel/type.h"

#define NIL_HOLE (struct hole *) 0

PRIVATE struct hole hole[_NR_HOLES];
PRIVATE u32_t high_watermark = 0;

PRIVATE struct hole *hole_head; /* pointer to first hole */
PRIVATE struct hole *free_slots;/* ptr to list of unused table slots */
#if ENABLE_SWAP
PRIVATE int swap_fd = -1;   /* file descriptor of open swap file/device */
PRIVATE u32_t swap_offset;  /* offset to start of swap area on swap file */
PRIVATE phys_clicks swap_base;  /* memory offset chosen as swap base */
PRIVATE phys_clicks swap_maxsize;/* maximum amount of swap "memory" possible */
PRIVATE struct mproc *in_queue; /* queue of processes wanting to swap in */
PRIVATE struct mproc *outswap = &mproc[0];   /* outswap candidate? */
#else /* ! ENABLE_SWAP */
#define swap_base ((phys_clicks) -1)
#endif /* ENABLE_SWAP */

FORWARD _PROTOTYPE( void del_slot, (struct hole *prev_ptr, struct hole *hp) );
FORWARD _PROTOTYPE( void merge, (struct hole *hp)               );
#if ENABLE_SWAP
FORWARD _PROTOTYPE( int swap_out, (void)                    );
#else
#define swap_out()  (0)
#endif
/*===========================================================================*
 *              alloc_mem               *
 *===========================================================================*/
PUBLIC phys_clicks alloc_mem(clicks) 
phys_clicks clicks;
{
    register struct hole *hp, *prev_ptr, *best, *prev_best;
    phys_clicks old_base,best_clicks;
    int flag=0;
    do {
        /* search from start */
        prev_ptr = NIL_HOLE;
        hp = hole_head;
        while (hp != NIL_HOLE && hp->h_base < swap_base){
            if (hp->h_len >= clicks){
                if (flag == 0){
                    /* first get fit block */
                    /* update */
                    best = hp;                      
                    prev_best = prev_ptr;           
                    best_clicks = hp->h_len;       
                    flag = 1;                  
                }else if (hp->h_len < best_clicks){
                    /* not first get fit block */ 
                    /* !!! this block is fitter */ 
                    /* update */ 
                    best = hp;
                    prev_best = prev_ptr;
                    best_clicks = hp->h_len;
                }
            }
            /* pass to next */ 
            prev_ptr = hp;
            hp = hp->h_next;
        }
    } while (swap_out());
    /* Try to find a process that can be swapped out.  
    Candidates are those blocked on a system call that PM handles, like wait(), pause() or sigsuspend().*/

    if (flag == 1) {
        old_base = best->h_base;
        /* update */     
        best->h_base += clicks;
        best->h_len -= clicks;

        if (best->h_base > high_watermark)high_watermark = best->h_base;
        if (best->h_len == 0)del_slot(prev_best,best); /* delete best */ 
        return(old_base);
    }
    return(NO_MEM);/* special condition - no mem */
}

/*===========================================================================*
 *              free_mem                     *
 *===========================================================================*/
PUBLIC void free_mem(base, clicks)
phys_clicks base;       /* base address of block to free */
phys_clicks clicks;     /* number of clicks to free */
{
/* Return a block of free memory to the hole list.  The parameters tell where
 * the block starts in physical memory and how big it is.  The block is added
 * to the hole list.  If it is contiguous with an existing hole on either end,
 * it is merged with the hole or holes.
 */
  register struct hole *hp, *new_ptr, *prev_ptr;

  if (clicks == 0) return;
  if ( (new_ptr = free_slots) == NIL_HOLE) 
    panic(__FILE__,"hole table full", NO_NUM);
  new_ptr->h_base = base;
  new_ptr->h_len = clicks;
  free_slots = new_ptr->h_next;
  hp = hole_head;

  /* If this block's address is numerically less than the lowest hole currently
   * available, or if no holes are currently available, put this hole on the
   * front of the hole list.
   */
  if (hp == NIL_HOLE || base <= hp->h_base) {
    /* Block to be freed goes on front of the hole list. */
    new_ptr->h_next = hp;
    hole_head = new_ptr;
    merge(new_ptr);
    return;
  }

  /* Block to be returned does not go on front of hole list. */
  prev_ptr = NIL_HOLE;
  while (hp != NIL_HOLE && base > hp->h_base) {
    prev_ptr = hp;
    hp = hp->h_next;
  }

  /* We found where it goes.  Insert block after 'prev_ptr'. */
  new_ptr->h_next = prev_ptr->h_next;
  prev_ptr->h_next = new_ptr;
  merge(prev_ptr);      /* sequence is 'prev_ptr', 'new_ptr', 'hp' */
}

/*===========================================================================*
 *              del_slot                     *
 *===========================================================================*/
PRIVATE void del_slot(prev_ptr, hp)
/* pointer to hole entry just ahead of 'hp' */
register struct hole *prev_ptr;
/* pointer to hole entry to be removed */
register struct hole *hp;   
{
/* Remove an entry from the hole list.  This procedure is called when a
 * request to allocate memory removes a hole in its entirety, thus reducing
 * the numbers of holes in memory, and requiring the elimination of one
 * entry in the hole list.
 */
  if (hp == hole_head)
    hole_head = hp->h_next;
  else
    prev_ptr->h_next = hp->h_next;

  hp->h_next = free_slots;
  hp->h_base = hp->h_len = 0;
  free_slots = hp;
}

/*===========================================================================*
 *              merge                        *
 *===========================================================================*/
PRIVATE void merge(hp)
register struct hole *hp;   /* ptr to hole to merge with its successors */
{
/* Check for contiguous holes and merge any found.  Contiguous holes can occur
 * when a block of memory is freed, and it happens to abut another hole on
 * either or both ends.  The pointer 'hp' points to the first of a series of
 * three holes that can potentially all be merged together.
 */
  register struct hole *next_ptr;

  /* If 'hp' points to the last hole, no merging is possible.  If it does not,
   * try to absorb its successor into it and free the successor's table entry.
   */
  if ( (next_ptr = hp->h_next) == NIL_HOLE) return;
  if (hp->h_base + hp->h_len == next_ptr->h_base) {
    hp->h_len += next_ptr->h_len;   /* first one gets second one's mem */
    del_slot(hp, next_ptr);
  } else {
    hp = next_ptr;
  }

  /* If 'hp' now points to the last hole, return; otherwise, try to absorb its
   * successor into it.
   */
  if ( (next_ptr = hp->h_next) == NIL_HOLE) return;
  if (hp->h_base + hp->h_len == next_ptr->h_base) {
    hp->h_len += next_ptr->h_len;
    del_slot(hp, next_ptr);
  }
}

/*===========================================================================*
 *              mem_init                     *
 *===========================================================================*/
PUBLIC void mem_init(chunks, free)
struct memory *chunks;      /* list of free memory chunks */
phys_clicks *free;      /* memory size summaries */
{
/* Initialize hole lists.  There are two lists: 'hole_head' points to a linked
 * list of all the holes (unused memory) in the system; 'free_slots' points to
 * a linked list of table entries that are not in use.  Initially, the former
 * list has one entry for each chunk of physical memory, and the second
 * list links together the remaining table slots.  As memory becomes more
 * fragmented in the course of time (i.e., the initial big holes break up into
 * smaller holes), new table slots are needed to represent them.  These slots
 * are taken from the list headed by 'free_slots'.
 */
  int i;
  register struct hole *hp;

  /* Put all holes on the free list. */
  for (hp = &hole[0]; hp < &hole[_NR_HOLES]; hp++) {
    hp->h_next = hp + 1;
    hp->h_base = hp->h_len = 0;
  }
  hole[_NR_HOLES-1].h_next = NIL_HOLE;
  hole_head = NIL_HOLE;
  free_slots = &hole[0];

  /* Use the chunks of physical memory to allocate holes. */
  *free = 0;
  for (i=NR_MEMS-1; i>=0; i--) {
    if (chunks[i].size > 0) {
        free_mem(chunks[i].base, chunks[i].size);
        *free += chunks[i].size;
#if ENABLE_SWAP
        if (swap_base < chunks[i].base + chunks[i].size) 
            swap_base = chunks[i].base + chunks[i].size;
#endif
    }
  }

#if ENABLE_SWAP
  /* The swap area is represented as a hole above and separate of regular
   * memory.  A hole at the size of the swap file is allocated on "swapon".
   */
  swap_base++;              /* make separate */
  swap_maxsize = 0 - swap_base;     /* maximum we can possibly use */
#endif
}

/*===========================================================================*
 *              mem_holes_copy                   *
 *===========================================================================*/
PUBLIC int mem_holes_copy(struct hole *holecopies, size_t *bytes, u32_t *hi)
{
    if(*bytes < sizeof(hole)) return ENOSPC;
    memcpy(holecopies, hole, sizeof(hole));
    *bytes = sizeof(hole);
    *hi = high_watermark;
    return OK;
}

#if ENABLE_SWAP
/*===========================================================================*
 *              swap_on                      *
 *===========================================================================*/
PUBLIC int swap_on(file, offset, size)
char *file;             /* file to swap on */
u32_t offset, size;         /* area on swap file to use */
{
/* Turn swapping on. */

  if (swap_fd != -1) return(EBUSY); /* already have swap? */

  tell_fs(CHDIR, who_e, FALSE, 0);  /* be like the caller for open() */
  if ((swap_fd = open(file, O_RDWR)) < 0) return(-errno);
  swap_offset = offset;
  size >>= CLICK_SHIFT;
  if (size > swap_maxsize) size = swap_maxsize;
  if (size > 0) free_mem(swap_base, (phys_clicks) size);
  return(OK);
}

/*===========================================================================*
 *              swap_off                     *
 *===========================================================================*/
PUBLIC int swap_off()
{
/* Turn swapping off. */
  struct mproc *rmp;
  struct hole *hp, *prev_ptr;

  if (swap_fd == -1) return(OK);    /* can't turn off what isn't on */

  /* Put all swapped out processes on the inswap queue and swap in. */
  for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
    if (rmp->mp_flags & ONSWAP) swap_inqueue(rmp);
  }
  swap_in();

  /* All in memory? */
  for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
    if (rmp->mp_flags & ONSWAP) return(ENOMEM);
  }

  /* Yes.  Remove the swap hole and close the swap file descriptor. */
  for (hp = hole_head; hp != NIL_HOLE; prev_ptr = hp, hp = hp->h_next) {
    if (hp->h_base >= swap_base) {
        del_slot(prev_ptr, hp);
        hp = hole_head;
    }
  }
  close(swap_fd);
  swap_fd = -1;
  return(OK);
}

/*===========================================================================*
 *              swap_inqueue                     *
 *===========================================================================*/
PUBLIC void swap_inqueue(rmp)
register struct mproc *rmp;     /* process to add to the queue */
{
/* Put a swapped out process on the queue of processes to be swapped in.  This
 * happens when such a process gets a signal, or if a reply message must be
 * sent, like when a process doing a wait() has a child that exits.
 */
  struct mproc **pmp;

  if (rmp->mp_flags & SWAPIN) return;   /* already queued */


  for (pmp = &in_queue; *pmp != NULL; pmp = &(*pmp)->mp_swapq) {}
  *pmp = rmp;
  rmp->mp_swapq = NULL;
  rmp->mp_flags |= SWAPIN;
}

/*===========================================================================*
 *              swap_in                      *
 *===========================================================================*/
PUBLIC void swap_in()
{
/* Try to swap in a process on the inswap queue.  We want to send it a message,
 * interrupt it, or something.
 */
  struct mproc **pmp, *rmp;
  phys_clicks old_base, new_base, size;
  off_t off;
  int proc_nr;

  pmp = &in_queue;
  while ((rmp = *pmp) != NULL) {
    proc_nr = (rmp - mproc);
    size = rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len
        - rmp->mp_seg[D].mem_vir;

    if (!(rmp->mp_flags & SWAPIN)) {
        /* Guess it got killed.  (Queue is cleaned here.) */
        *pmp = rmp->mp_swapq;
        continue;
    } else
    if ((new_base = alloc_mem(size)) == NO_MEM) {
        /* No memory for this one, try the next. */
        pmp = &rmp->mp_swapq;
    } else {
        /* We've found memory.  Update map and swap in. */
        old_base = rmp->mp_seg[D].mem_phys;
        rmp->mp_seg[D].mem_phys = new_base;
        rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + 
            (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
        sys_newmap(rmp->mp_endpoint, rmp->mp_seg);
        off = swap_offset + ((off_t) (old_base-swap_base)<<CLICK_SHIFT);
        lseek(swap_fd, off, SEEK_SET);
        rw_seg(0, swap_fd, rmp->mp_endpoint, D, (phys_bytes)size << CLICK_SHIFT);
        free_mem(old_base, size);
        rmp->mp_flags &= ~(ONSWAP|SWAPIN);
        *pmp = rmp->mp_swapq;
        check_pending(rmp); /* a signal may have waked this one */
    }
  }
}

/*===========================================================================*
 *              swap_out                     *
 *===========================================================================*/
PRIVATE int swap_out()
{
/* Try to find a process that can be swapped out.  Candidates are those blocked
 * on a system call that PM handles, like wait(), pause() or sigsuspend().
 */
  struct mproc *rmp;
  struct hole *hp, *prev_ptr;
  phys_clicks old_base, new_base, size;
  off_t off;
  int proc_nr;

  rmp = outswap;
  do {
    if (++rmp == &mproc[NR_PROCS]) rmp = &mproc[0];

    /* A candidate? */
    if (!(rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED))) continue;

    /* Already on swap or otherwise to be avoided? */
    if (rmp->mp_flags & (DONT_SWAP | TRACED | REPLY | ONSWAP)) continue;

    /* Got one, find a swap hole and swap it out. */
    proc_nr = (rmp - mproc);
    size = rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len
        - rmp->mp_seg[D].mem_vir;

    prev_ptr = NIL_HOLE;
    for (hp = hole_head; hp != NIL_HOLE; prev_ptr = hp, hp = hp->h_next) {
        if (hp->h_base >= swap_base && hp->h_len >= size) break;
    }
    if (hp == NIL_HOLE) continue;   /* oops, not enough swapspace */
    new_base = hp->h_base;
    hp->h_base += size;
    hp->h_len -= size;
    if (hp->h_len == 0) del_slot(prev_ptr, hp);

    off = swap_offset + ((off_t) (new_base - swap_base) << CLICK_SHIFT);
    lseek(swap_fd, off, SEEK_SET);
    rw_seg(1, swap_fd, rmp->mp_endpoint, D, (phys_bytes)size << CLICK_SHIFT);
    old_base = rmp->mp_seg[D].mem_phys;
    rmp->mp_seg[D].mem_phys = new_base;
    rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + 
        (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
    sys_newmap(rmp->mp_endpoint, rmp->mp_seg);
    free_mem(old_base, size);
    rmp->mp_flags |= ONSWAP;

    outswap = rmp;      /* next time start here */
    return(TRUE);
  } while (rmp != outswap);

  return(FALSE);    /* no candidate found */
}
#endif /* SWAP */

break.c

/* The MINIX model of memory allocation reserves a fixed amount of memory for
 * the combined text, data, and stack segments.  The amount used for a child
 * process created by FORK is the same as the parent had.  If the child does
 * an EXEC later, the new size is taken from the header of the file EXEC'ed.
 *
 * The layout in memory consists of the text segment, followed by the data
 * segment, followed by a gap (unused memory), followed by the stack segment.
 * The data segment grows upward and the stack grows downward, so each can
 * take memory from the gap.  If they meet, the process must be killed.  The
 * procedures in this file deal with the growth of the data and stack segments.
 *
 * The entry points into this file are:
 *   do_brk:      BRK/SBRK system calls to grow or shrink the data segment
 *   adjust:      see if a proposed segment adjustment is allowed
 *   size_ok:     see if the segment sizes are feasible (i86 only)
 */

#include "pm.h"
#include <signal.h>
#include "mproc.h"
#include "param.h"
#include <lib.h>
#define DATA_CHANGED       1    /* flag value when data segment size changed */
#define STACK_CHANGED      2    /* flag value when stack size changed */

/*===========================================================================*
 *              do_brk                       *
 *===========================================================================*/
PUBLIC int do_brk()
{
/* Perform the brk(addr) system call.
 *
 * The call is complicated by the fact that on some machines (e.g., 8088),
 * the stack pointer can grow beyond the base of the stack segment without
 * anybody noticing it.
 * The parameter, 'addr' is the new virtual address in D space.
 */

  register struct mproc *rmp;
  int r;
  vir_bytes v, new_sp;
  vir_clicks new_clicks;

  rmp = mp;
  v = (vir_bytes) m_in.addr;
  new_clicks = (vir_clicks) ( ((long) v + CLICK_SIZE - 1) >> CLICK_SHIFT);
  if (new_clicks < rmp->mp_seg[D].mem_vir) {
    rmp->mp_reply.reply_ptr = (char *) -1;
    return(ENOMEM);
  }
  new_clicks -= rmp->mp_seg[D].mem_vir;
  if ((r=get_stack_ptr(who_e, &new_sp)) != OK) /* ask kernel for sp value */
    panic(__FILE__,"couldn't get stack pointer", r);
  r = adjust(rmp, new_clicks, new_sp);
  rmp->mp_reply.reply_ptr = (r == OK ? m_in.addr : (char *) -1);
  return(r);            /* return new address or -1 */
}

/*===========================================================================*
 *              allocate_new_mem                     *
 *===========================================================================*/
PUBLIC int allocate_new_mem(rmp,clicks) 
register struct mproc *rmp; 
phys_clicks clicks; {
    /* Process Management Table */
    /*clicks: old clicks*/
    int copy_stack,copy_data;
    register struct mem_map *mem_sp, *mem_dp;/* point to stack,data part */
    /* we mainly expand the data segment, not stack */
    /* data part */
    phys_clicks old_data_clicks,new_data_clicks;
    phys_clicks old_data_base,new_data_base;
    /* stack part */
    phys_clicks stack_clicks;
    phys_clicks old_stack_base,new_stack_base;

    /* convert to bytes version */
    phys_bytes old_data_bytes, new_data_bytes;
    phys_bytes stak_bytes;
    phys_bytes old_data_base_bytes, new_data_base_bytes;
    phys_bytes old_stack_base_bytes, new_stack_base_bytes;


    mem_dp = &rmp->mp_seg[D]; /* point to data part */
    mem_sp = &rmp->mp_seg[S]; /* point to stack part */
    old_data_clicks = clicks;
    new_data_clicks = clicks*2;
    stack_clicks = mem_sp->mem_len;
    /* use alloc_mem */
    /* if fail, don't free */
    if ((new_data_base = alloc_mem(new_data_clicks))==NO_MEM){
        /* printf("allocate error!"); */
        return (ENOMEM);
    }


    /* convert click -> byte */
    new_data_bytes = (phys_bytes) rmp->mp_seg[D].mem_len << CLICK_SHIFT;
    stak_bytes = (phys_bytes) rmp->mp_seg[S].mem_len << CLICK_SHIFT;

    old_data_base = rmp->mp_seg[D].mem_phys;
    old_stack_base = rmp->mp_seg[S].mem_phys;

    old_data_base_bytes = (phys_bytes)old_data_base << CLICK_SHIFT;
    old_stack_base_bytes = (phys_bytes)old_stack_base << CLICK_SHIFT;


    /* update */
    new_stack_base = new_data_base + new_data_clicks - stack_clicks;
    new_stack_base_bytes = (phys_bytes)new_stack_base << CLICK_SHIFT;
    new_data_base_bytes = (phys_bytes) new_data_base << CLICK_SHIFT;


    sys_memset(0,new_data_base_bytes,(new_data_clicks<<CLICK_SHIFT));

    copy_data = sys_abscopy(old_data_base_bytes,new_data_base_bytes,new_data_bytes);  
    if (copy_data < 0) panic(__FILE__,"allocate_new_mem can't copy",copy_data); 
    copy_stack = sys_abscopy(old_stack_base_bytes,new_stack_base_bytes,stak_bytes); 
    if (copy_stack < 0) panic(__FILE__,"allocate_new_mem can't copy",copy_stack); 

    rmp->mp_seg[D].mem_phys = new_data_base;

    rmp->mp_seg[S].mem_phys = new_stack_base; 
    rmp->mp_seg[S].mem_vir = rmp->mp_seg[D].mem_vir + new_data_clicks - mem_sp->mem_len;

    free_mem(old_data_base,old_data_clicks); 
    return (OK);
}


/*===========================================================================*
 *              adjust                       *
 *===========================================================================*/
PUBLIC int adjust(rmp, data_clicks, sp)
register struct mproc *rmp; /* whose memory is being adjusted? */
vir_clicks data_clicks;     /* how big is data segment to become? */
vir_bytes sp;           /* new value of sp */
{
/* See if data and stack segments can coexist, adjusting them if need be.
 * Memory is never allocated or freed.  Instead it is added or removed from the
 * gap between data segment and stack segment.  If the gap size becomes
 * negative, the adjustment of data or stack fails and ENOMEM is returned.
 */

  register struct mem_map *mem_sp, *mem_dp;
  vir_clicks sp_click, gap_base, lower, old_clicks;
  int changed, r, ft;
  long base_of_stack, delta;    /* longs avoid certain problems */

  mem_dp = &rmp->mp_seg[D]; /* pointer to data segment map */
  mem_sp = &rmp->mp_seg[S]; /* pointer to stack segment map */
  changed = 0;          /* set when either segment changed */

  if (mem_sp->mem_len == 0) return(OK); /* don't bother init */

  /* See if stack size has gone negative (i.e., sp too close to 0xFFFF...) */
  base_of_stack = (long) mem_sp->mem_vir + (long) mem_sp->mem_len;
  sp_click = sp >> CLICK_SHIFT; /* click containing sp */
  if (sp_click >= base_of_stack) return(ENOMEM);    /* sp too high */

  /* Compute size of gap between stack and data segments. */
  delta = (long) mem_sp->mem_vir - (long) sp_click;
  lower = (delta > 0 ? sp_click : mem_sp->mem_vir);//栈的起始位置

  /* Add a safety margin for future stack growth. Impossible to do right. */
#define SAFETY_BYTES  (384 * sizeof(char *))
#define SAFETY_CLICKS ((SAFETY_BYTES + CLICK_SIZE - 1) / CLICK_SIZE)
  gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS;
  if (lower < gap_base) /* data and stack collided */    
  if(allocate_new_mem(rmp,(phys_clicks)(rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir + rmp->mp_seg[S].mem_len)))
        return(ENOMEM); 

  /* Update data length (but not data orgin) on behalf of brk() system call. */
  old_clicks = mem_dp->mem_len;
  if (data_clicks != mem_dp->mem_len) {
    mem_dp->mem_len = data_clicks;
    changed |= DATA_CHANGED;
  }

  /* Update stack length and origin due to change in stack pointer. */
  if (delta > 0) {
    mem_sp->mem_vir -= delta;
    mem_sp->mem_phys -= delta;
    mem_sp->mem_len += delta;
    changed |= STACK_CHANGED;
  }

  /* Do the new data and stack segment sizes fit in the address space? */
  ft = (rmp->mp_flags & SEPARATE);
#if (CHIP == INTEL && _WORD_SIZE == 2)
  r = size_ok(ft, rmp->mp_seg[T].mem_len, rmp->mp_seg[D].mem_len, 
       rmp->mp_seg[S].mem_len, rmp->mp_seg[D].mem_vir, rmp->mp_seg[S].mem_vir);
#else
  r = (rmp->mp_seg[D].mem_vir + rmp->mp_seg[D].mem_len > 
          rmp->mp_seg[S].mem_vir) ? ENOMEM : OK;
#endif
  if (r == OK) {
    int r2;
    if (changed && (r2=sys_newmap(rmp->mp_endpoint, rmp->mp_seg)) != OK)
        panic(__FILE__,"couldn't sys_newmap in adjust", r2);
    return(OK);
  }

  /* New sizes don't fit or require too many page/segment registers. Restore.*/
  if (changed & DATA_CHANGED) mem_dp->mem_len = old_clicks;
  if (changed & STACK_CHANGED) {
    mem_sp->mem_vir += delta;
    mem_sp->mem_phys += delta;
    mem_sp->mem_len -= delta;
  }
  return(ENOMEM);
}

#if (CHIP == INTEL && _WORD_SIZE == 2)
/*===========================================================================*
 *              size_ok                      *
 *===========================================================================*/
PUBLIC int size_ok(file_type, tc, dc, sc, dvir, s_vir)
int file_type;          /* SEPARATE or 0 */
vir_clicks tc;          /* text size in clicks */
vir_clicks dc;          /* data size in clicks */
vir_clicks sc;          /* stack size in clicks */
vir_clicks dvir;        /* virtual address for start of data seg */
vir_clicks s_vir;       /* virtual address for start of stack seg */
{
/* Check to see if the sizes are feasible and enough segmentation registers
 * exist.  On a machine with eight 8K pages, text, data, stack sizes of
 * (32K, 16K, 16K) will fit, but (33K, 17K, 13K) will not, even though the
 * former is bigger (64K) than the latter (63K).  Even on the 8088 this test
 * is needed, since the data and stack may not exceed 4096 clicks.
 * Note this is not used for 32-bit Intel Minix, the test is done in-line.
 */

  int pt, pd, ps;       /* segment sizes in pages */

  pt = ( (tc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
  pd = ( (dc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
  ps = ( (sc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;

  if (file_type == SEPARATE) {
    if (pt > MAX_PAGES || pd + ps > MAX_PAGES) return(ENOMEM);
  } else {
    if (pt + pd + ps > MAX_PAGES) return(ENOMEM);
  }

  if (dvir + dc > s_vir) return(ENOMEM);

  return(OK);
}
#endif