[comp.os.minix] Multiple process priorities.

tih@uunet.uu.net (01/24/89)

(This seems not to have made it to the list the last time I posted
it, so I'm resending it by a different route this time.)

Context Diffs to give Minix 1.3c multiple user-process priority levels.
----------------------------------------------------------------------

I'm getting to be pretty happy with the way Minix works for me...  I've
now got the root file system on a hard disk partition, with enough on
it to let me umount the /usr partition for administrative work like
doing backups.  That was, of course, just a few lines of code added to
fs/main.c, as per an article posted to this group some time back.

The latest thing is more priority levels for user processes.  It's nice
to be able to run processes in the background, whether they're daemons
or compilations or whatever, but I didn't like the sluggish response
Minix gave me while such processes were running, and decided to do
something about it.

The result accompanies this message as concatenated cdiffs for the
minix/kernel directory.  I wanted to implement the 'nice' system call,
but decided that this would affect too many files in too many places.
Better to wait for an official way of handling priorities first...

What I did was quite simple.  I added a new field, p_pri, to the
struct proc used by the kernel, and let this hold a priority number
ranging from 0 to 7.  These numbers coincide exactly with the number
of the scheduling queues used for these priority levels, so that the
kernel tasks have priority 0, MM and FS have 1, and priorities from
2 through 7 are for user processes.  My modifications to the kernel
files were done in the space of 2 hours 'burning the midnight oil',
and I'm well aware that what I did to proc.c isn't as elegant or as
effective as it might be.  However, I'd very much like for some of you
to try this out, get hooked, and decide that we do need more than one
user priority level.  Then we might discuss how best to do that, and
ask Andy Tanenbaum to incorporate a solution in the 'official' Minix.

Since I didn't implement a 'nice' call, what I did instead was a quick
hack that worked so well it surprised me.  :-)  I modified the 'fork'
call to lower the priority of the child by 1, because I figured this
would make background processes less noticable.  The result of this
is that INIT now runs at priority 2, the login programs and login shells
at priority 3, daemons started from rc with & (like update) at level
4, daemons that fork off their own children in the background (like
cron) at level 5, user programs started directly from the shell at
level 4, and background user programs (like make &) will operate at
varying levels.  When doing 'make &', make runs at level 4, a shell at
5, cc at 6, and the compiler passes at level 7.  To test this, I
cd'ed to the minix/amoeba directory and did a 'make > makelog 2>&1 &'
there, which ran in the background while I did ls -l, df, cat makelog,
and the like.  Nice, quick response all the time.

Of course, when I started using mined with that kernel remake in the
background the make aborted because it couldn't run /lib/cem -- anyone
want to volunteer to implement swapping?  :-)

Anyway, I hope some of you will try this stuff out, and give some
feedback.

-tih

------ Cut here for cdiffs -------------------------------------------

*** const.h.old	Thu Dec 29 19:55:27 1988
--- const.h	Thu Dec 29 22:45:04 1988
***************
*** 47,55 ****
  #define IDLE            -999	/* 'cur_proc' = IDLE means nobody is running */
  
  /* The following items pertain to the 3 scheduling queues. */
! #define NQ                 3	/* # of scheduling queues */
! #define TASK_Q             0	/* ready tasks are scheduled via queue 0 */
! #define SERVER_Q           1	/* ready servers are scheduled via queue 1 */
! #define USER_Q             2	/* ready users are scheduled via queue 2 */
! 
! #define printf        printk	/* the kernel really uses printk, not printf */
--- 47,56 ----
  #define IDLE            -999	/* 'cur_proc' = IDLE means nobody is running */
  
  /* The following items pertain to the 3 scheduling queues. */
! #define NQ                 8	/* # of scheduling queues */
! #define TASK_Q             0	/* ready tasks are scheduled via queue 0 */
! #define SERVER_Q           1	/* ready servers are scheduled via queue 1 */
! #define USER_Q             2	/* ready users via queues 2 and up */
! #define LAST_Q             7	/* last user process queue */
! 
! #define printf        printk	/* the kernel really uses printk, not printf */

*** dmp.c.old	Thu Dec 29 20:03:05 1988
--- dmp.c	Fri Dec 30 01:38:15 1988
***************
*** 31,37 ****
    extern phys_bytes umap();
  
    printf(
!  "\nproc  -pid- -pc-  -sp-  flag  pri  user  -sys-  base limit recv   command\r\n");
  
    dst = umap(proc_addr(SYSTASK), D, (vir_bytes)nbuff, NSIZE);
  
--- 31,37 ----
    extern phys_bytes umap();
  
    printf(
!  "\nproc   pid  -pc- -sp- flag prio  user  system  base limit recv  command\r\n");
  
    dst = umap(proc_addr(SYSTASK), D, (vir_bytes)nbuff, NSIZE);
  
***************
*** 44,51 ****
  	ltmp = (((long) last << 4) + 512L);
  	limit = (vir_bytes) (ltmp/1024L);
  	prname(rp - proc);
! 	printf(" %4d %4x %4x %4x %6D %7D  %3dK %3dK  ",
! 		rp->p_pid, rp->p_pcpsw.pc, rp->p_sp, rp->p_flags,
  		rp->user_time, rp->sys_time,
  		base, limit);
  		if (rp->p_flags == 0)
--- 44,52 ----
  	ltmp = (((long) last << 4) + 512L);
  	limit = (vir_bytes) (ltmp/1024L);
  	prname(rp - proc);
! 	printf(" %4d %4x %4x %3x %4x %6D %7D  %3dK  %3dK ",
! 		rp->p_pid, rp->p_pcpsw.pc, rp->p_sp, rp->p_flags,
! 		rp->p_pri,
  		rp->user_time, rp->sys_time,
  		base, limit);
  		if (rp->p_flags == 0)

*** main.c.old	Thu Dec 29 20:53:16 1988
--- main.c	Thu Dec 29 23:06:48 1988
***************
*** 96,101 ****
--- 96,107 ----
  		rp->p_splimit = rp->p_sp;
  	}
  	rp->p_pcpsw.pc = tasktab[t + NR_TASKS].initial_pc;
+ 	if(t < 0)
+ 		rp->p_pri = TASK_Q;
+ 	else if(t < 2)
+ 		rp->p_pri = SERVER_Q;
+ 	else
+ 		rp->p_pri = USER_Q;
  	if (rp->p_pcpsw.pc != 0 || t >= 0) ready(rp);
  	rp->p_pcpsw.psw = INIT_PSW;
  	rp->p_flags = 0;

*** proc.c.old	Thu Dec 29 19:55:40 1988
--- proc.c	Fri Dec 30 00:04:27 1988
***************
*** 261,267 ****
  
    if (rdy_head[TASK_Q] != NIL_PROC) q = TASK_Q;
    else if (rdy_head[SERVER_Q] != NIL_PROC) q = SERVER_Q;
!   else q = USER_Q;
  
    /* Set 'cur_proc' and 'proc_ptr'. If system is idle, set 'cur_proc' to a
     * special value (IDLE), and set 'proc_ptr' to point to an unused proc table
--- 261,272 ----
  
    if (rdy_head[TASK_Q] != NIL_PROC) q = TASK_Q;
    else if (rdy_head[SERVER_Q] != NIL_PROC) q = SERVER_Q;
!   else
! 	for (q = USER_Q; q < NQ; q++)
! 		if (rdy_head[q] != NIL_PROC)
! 			break;
!   if (q == NQ)
! 	q = USER_Q;
  
    /* Set 'cur_proc' and 'proc_ptr'. If system is idle, set 'cur_proc' to a
     * special value (IDLE), and set 'proc_ptr' to point to an unused proc table
***************
*** 297,302 ****
--- 302,309 ----
   *   TASK_Q   - (highest priority) for runnable tasks
   *   SERVER_Q - (middle priority) for MM and FS only
   *   USER_Q   - (lowest priority) for user processes
+  * The last of these queues is actually 6 queues -- we now handle
+  * priorities from 0 - 7, with users ranging from 2 - 7.
   */
  
    register int q;		/* TASK_Q, SERVER_Q, or USER_Q */
***************
*** 305,310 ****
--- 312,319 ----
    old_state = lock();		/* disable interrupts */
    r = (rp - proc) - NR_TASKS;	/* task or proc number */
    q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);
+   if (q == USER_Q)
+ 	q = rp->p_pri;	/* priority says what queue to use */
  
    /* See if the relevant queue is empty. */
    if (rdy_head[q] == NIL_PROC)
***************
*** 326,356 ****
  /* A process has blocked. */
  
    register struct proc *xp;
!   int r, q, old_state;
  
    old_state = lock();			/* disable interrupts */
    r = rp - proc - NR_TASKS;
    q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);
!   if ( (xp = rdy_head[q]) == NIL_PROC) {
! 	restore(old_state);
! 	return;
!   }
!   if (xp == rp) {
! 	/* Remove head of queue */
! 	rdy_head[q] = xp->p_nextready;
! 	if (rp == proc_ptr) pick_proc();
!   } else {
! 	/* Search body of queue.  A process can be made unready even if it is
! 	 * not running by being sent a signal that kills it.
! 	 */
! 	while (xp->p_nextready != rp)
! 		if ( (xp = xp->p_nextready) == NIL_PROC) {
! 			restore(old_state);
! 			return;
! 		}
! 	xp->p_nextready = xp->p_nextready->p_nextready;
! 	while (xp->p_nextready != NIL_PROC) xp = xp->p_nextready;
! 	rdy_tail[q] = xp;
    }
    restore(old_state);			/* restore interrupts to prev state */
  }
--- 335,385 ----
  /* A process has blocked. */
  
    register struct proc *xp;
!   int st, li, r, q, old_state;
  
    old_state = lock();			/* disable interrupts */
    r = rp - proc - NR_TASKS;
    q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);
! 
!   if (q == USER_Q) {
! 	st = USER_Q;
! 	li = NQ;
!   } else {
! 	st = q;
! 	li = q + 1;
!   }
! 
!   /* This has been made into a loop, because a user process may have
!      received a new priority after being put on a queue.  This switch
!      will not be noticed until next time the process is readied.
!   */
! 
!   for (q = st; q < li; q++) {
! 	if ( (xp = rdy_head[q]) == NIL_PROC) {
! 		continue;
! 	}
! 	if (xp == rp) {
! 		/* Remove head of queue */
! 		rdy_head[q] = xp->p_nextready;
! 		if (rp == proc_ptr) {
! 			pick_proc();
! 			break;
! 		}
! 	} else {
! 		/* Search body of queue.  A process can be made unready even if it is
! 		 * not running by being sent a signal that kills it.
! 		 */
! 		while (xp->p_nextready != rp)
! 			if ( (xp = xp->p_nextready) == NIL_PROC) {
! 				break;
! 			}
! 		if (xp == NIL_PROC)
! 			continue;
! 		xp->p_nextready = xp->p_nextready->p_nextready;
! 		while (xp->p_nextready != NIL_PROC) xp = xp->p_nextready;
! 		rdy_tail[q] = xp;
! 		break;
! 	}
    }
    restore(old_state);			/* restore interrupts to prev state */
  }
***************
*** 366,384 ****
   * possibly promoting another user to head of the queue.
   */
  
!   int old_state;
! 
!   old_state = lock();			/* disable interrupts */
!   if (rdy_head[USER_Q] == NIL_PROC) {
  	restore(old_state);		/* restore interrupts to prev state */
  	return;
    }
  
!   /* One or more user processes queued. */
!   rdy_tail[USER_Q]->p_nextready = rdy_head[USER_Q];
!   rdy_tail[USER_Q] = rdy_head[USER_Q];
!   rdy_head[USER_Q] = rdy_head[USER_Q]->p_nextready;
!   rdy_tail[USER_Q]->p_nextready = NIL_PROC;
!   pick_proc();
!   restore(old_state);			/* restore interrupts to prev state */
! }
--- 395,429 ----
   * possibly promoting another user to head of the queue.
   */
  
!   int q, pri, old_state;
!   struct proc *pp;
! 
!   old_state = lock();			/* disable interrupts */
! 
!   for (q = USER_Q; q < NQ; q++)
! 	if (rdy_head[q] == proc_ptr)
! 		break;
! 
!   if (q == NQ) {
  	restore(old_state);		/* restore interrupts to prev state */
  	return;
    }
  
!   pri = proc_ptr->p_pri;
!   pp = rdy_head[q];
!   rdy_head[q] = rdy_head[q]->p_nextready;
!   if(rdy_head[q] == NIL_PROC)
! 	rdy_tail[q] = NIL_PROC;
! 
!   if (rdy_tail[pri] == NIL_PROC)
! 	rdy_tail[pri] = pp;
!   else
! 	rdy_tail[pri]->p_nextready = pp;
!   if (rdy_head[pri] == NIL_PROC)
! 	rdy_head[pri] = rdy_tail[pri];
!   rdy_tail[pri] = pp;
!   pp->p_nextready = NIL_PROC;
! 
!   pick_proc();
!   restore(old_state);			/* restore interrupts to prev state */
! }

*** proc.h.old	Thu Dec 29 19:55:33 1988
--- proc.h	Thu Dec 29 22:43:34 1988
***************
*** 27,32 ****
--- 27,34 ----
  
    struct proc *p_nextready;	/* pointer to next ready process */
    int p_pending;		/* bit map for pending signals 1-16 */
+   int p_pri;			/* priority of process */
+ 
  } proc[NR_TASKS+NR_PROCS];
  
  /* Bits for p_flags in proc[].  A process is runnable iff p_flags == 0 */

*** system.c.old	Thu Dec 29 22:50:42 1988
--- system.c	Fri Dec 30 00:50:30 1988
***************
*** 145,150 ****
--- 145,158 ----
    rpc->sys_time = 0;
    rpc->child_utime = 0;
    rpc->child_stime = 0;
+ 
+   if (rpc->p_pri < USER_Q)
+ 	rpc->p_pri = USER_Q;
+ 
+ /* TEMPORARY!!!  This lowers priority every fork, until we have nice */
+   if (rpc->p_pri < LAST_Q)
+ 	rpc->p_pri++;
+ 
    return(OK);
  }
  
***************
*** 219,224 ****
--- 227,235 ----
    rp->p_flags &= ~RECEIVING;	/* MM does not reply to EXEC call */
    if (rp->p_flags == 0) ready(rp);
    set_name(k, (char *)sp);	/* save command string for F1 display */
+   if (rp->p_pri < USER_Q)
+ 	rp->p_pri = USER_Q;
+ 
    return(OK);
  }
  
------ End of cdiffs -------------------------------------------------
----------------------------------------------------------------------------
Tom Ivar Helbekkmo                                ..!mcvax!ndosl!melkart!tih
Fredrik Meltzers gt 11      Standard                 thelbekk@norunit.bitnet
N-5007 Bergen                   disclaimers            helbekkmo@nhh.uninett
NORWAY                        apply...
Phone: +47-5-960561                             MS-DOS & OS/2?  Just say NO!
----------------------------------------------------------------------------