svn commit: r209600 - head/bin/sh

Jilles Tjoelker jilles at FreeBSD.org
Tue Jun 29 22:37:46 UTC 2010


Author: jilles
Date: Tue Jun 29 22:37:45 2010
New Revision: 209600
URL: http://svn.freebsd.org/changeset/base/209600

Log:
  sh: Forget about terminated background processes sooner.
  
  Unless $! has been referenced for a particular job or $! still contains that
  job's pid, forget about it after it has terminated. If $! has been
  referenced, remember the job until the wait builtin has reported its
  completion (either with the pid as parameter or without parameters).
  
  In interactive mode, jobs are forgotten after termination has been reported,
  which happens before primary prompts and through the jobs builtin. Even
  then, though, remember a job if $! has been referenced.
  
  This is similar to what is suggested by POSIX and should fix most memory
  leaks (which also tend to cause sh to use more CPU time) with long running
  scripts that start background jobs.
  
  Caveats:
  * Repeatedly referencing $! without ever doing 'wait', like
      while :; do foo & echo started foo: $!; sleep 60; done
    will still use a lot of memory and CPU time in the long run.
  * The jobs and jobid builtins do not cause a job to be remembered for longer
    like expanding $! does.
  
  PR:		bin/55346

Modified:
  head/bin/sh/expand.c
  head/bin/sh/jobs.c
  head/bin/sh/jobs.h
  head/bin/sh/sh.1

Modified: head/bin/sh/expand.c
==============================================================================
--- head/bin/sh/expand.c	Tue Jun 29 22:07:53 2010	(r209599)
+++ head/bin/sh/expand.c	Tue Jun 29 22:37:45 2010	(r209600)
@@ -818,7 +818,7 @@ varisset(char *name, int nulok)
 {
 
 	if (*name == '!')
-		return backgndpid != -1;
+		return backgndpidset();
 	else if (*name == '@' || *name == '*') {
 		if (*shellparam.p == NULL)
 			return 0;
@@ -891,7 +891,7 @@ varvalue(char *name, int quoted, int sub
 		num = shellparam.nparam;
 		goto numvar;
 	case '!':
-		num = backgndpid;
+		num = backgndpidval();
 numvar:
 		expdest = cvtnum(num, expdest);
 		break;

Modified: head/bin/sh/jobs.c
==============================================================================
--- head/bin/sh/jobs.c	Tue Jun 29 22:07:53 2010	(r209599)
+++ head/bin/sh/jobs.c	Tue Jun 29 22:37:45 2010	(r209600)
@@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$");
 STATIC struct job *jobtab;	/* array of jobs */
 STATIC int njobs;		/* size of array */
 MKINIT pid_t backgndpid = -1;	/* pid of last background process */
+MKINIT struct job *bgjob = NULL; /* last background process */
 #if JOBS
 STATIC struct job *jobmru;	/* most recently used job list */
 STATIC pid_t initialpgrp;	/* pgrp of shell on invocation */
@@ -183,6 +184,7 @@ INCLUDE <stdlib.h>
 
 SHELLPROC {
 	backgndpid = -1;
+	bgjob = NULL;
 #if JOBS
 	jobctl = 0;
 #endif
@@ -413,7 +415,11 @@ showjobs(int change, int mode)
 			continue;
 		showjob(jp, 0, mode);
 		jp->changed = 0;
-		if (jp->state == JOBDONE) {
+		/* Hack: discard jobs for which $! has not been referenced
+		 * in interactive mode when they terminate.
+		 */
+		if (jp->state == JOBDONE && !jp->remembered &&
+				(iflag || jp != bgjob)) {
 			freejob(jp);
 		}
 	}
@@ -431,6 +437,8 @@ freejob(struct job *jp)
 	int i;
 
 	INTOFF;
+	if (bgjob == jp)
+		bgjob = NULL;
 	for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
 		if (ps->cmd != nullstr)
 			ckfree(ps->cmd);
@@ -477,12 +485,27 @@ waitcmd(int argc, char **argv)
 #endif
 				else
 					retval = WTERMSIG(status) + 128;
-				if (! iflag)
+				if (! iflag || ! job->changed)
 					freejob(job);
+				else {
+					job->remembered = 0;
+					if (job == bgjob)
+						bgjob = NULL;
+				}
 				in_waitcmd--;
 				return retval;
 			}
 		} else {
+			for (jp = jobtab ; jp < jobtab + njobs; jp++)
+				if (jp->used && jp->state == JOBDONE) {
+					if (! iflag || ! jp->changed)
+						freejob(jp);
+					else {
+						jp->remembered = 0;
+						if (jp == bgjob)
+							bgjob = NULL;
+					}
+				}
 			for (jp = jobtab ; ; jp++) {
 				if (jp >= jobtab + njobs) {	/* no running procs */
 					in_waitcmd--;
@@ -623,6 +646,8 @@ makejob(union node *node __unused, int n
 						jp[i].next = &jp[jp[i].next -
 						    jobtab];
 #endif
+				if (bgjob != NULL)
+					bgjob = &jp[bgjob - jobtab];
 				/* Relocate `ps' pointers */
 				for (i = 0; i < njobs; i++)
 					if (jp[i].ps == &jobtab[i].ps0)
@@ -644,6 +669,7 @@ makejob(union node *node __unused, int n
 	jp->changed = 0;
 	jp->nprocs = 0;
 	jp->foreground = 0;
+	jp->remembered = 0;
 #if JOBS
 	jp->jobctl = jobctl;
 	jp->next = NULL;
@@ -821,8 +847,13 @@ forkshell(struct job *jp, union node *n,
 			pgrp = jp->ps[0].pid;
 		setpgid(pid, pgrp);
 	}
-	if (mode == FORK_BG)
+	if (mode == FORK_BG) {
+		if (bgjob != NULL && bgjob->state == JOBDONE &&
+		    !bgjob->remembered && !iflag)
+			freejob(bgjob);
 		backgndpid = pid;		/* set $! */
+		bgjob = jp;
+	}
 	if (jp) {
 		struct procstat *ps = &jp->ps[jp->nprocs++];
 		ps->pid = pid;
@@ -975,10 +1006,15 @@ dowait(int block, struct job *job)
 				if (jp->state != state) {
 					TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
 					jp->state = state;
+					if (jp != job) {
+						if (done && !jp->remembered &&
+						    !iflag && jp != bgjob)
+							freejob(jp);
 #if JOBS
-					if (done)
-						deljob(jp);
+						else if (done)
+							deljob(jp);
 #endif
+					}
 				}
 			}
 		}
@@ -1074,6 +1110,21 @@ checkzombies(void)
 }
 
 
+int
+backgndpidset(void)
+{
+	return backgndpid != -1;
+}
+
+
+pid_t
+backgndpidval(void)
+{
+	if (bgjob != NULL)
+		bgjob->remembered = 1;
+	return backgndpid;
+}
+
 /*
  * Return a string identifying a command (to be printed by the
  * jobs command.

Modified: head/bin/sh/jobs.h
==============================================================================
--- head/bin/sh/jobs.h	Tue Jun 29 22:07:53 2010	(r209599)
+++ head/bin/sh/jobs.h	Tue Jun 29 22:37:45 2010	(r209600)
@@ -68,6 +68,7 @@ struct job {
 	char used;		/* true if this entry is in used */
 	char changed;		/* true if status has changed */
 	char foreground;	/* true if running in the foreground */
+	char remembered;	/* true if $! referenced */
 #if JOBS
 	char jobctl;		/* job running under job control */
 	struct job *next;	/* job used after this one */
@@ -81,7 +82,6 @@ enum {
 	SHOWJOBS_PGIDS		/* PID of the group leader only */
 };
 
-extern pid_t backgndpid;	/* pid of last background process */
 extern int job_warning;		/* user was warned about stopped jobs */
 extern int in_waitcmd;		/* are we in waitcmd()? */
 extern int in_dowait;		/* are we in dowait()? */
@@ -98,6 +98,8 @@ struct job *makejob(union node *, int);
 pid_t forkshell(struct job *, union node *, int);
 int waitforjob(struct job *, int *);
 int stoppedjobs(void);
+int backgndpidset(void);
+pid_t backgndpidval(void);
 char *commandtext(union node *);
 
 #if ! JOBS

Modified: head/bin/sh/sh.1
==============================================================================
--- head/bin/sh/sh.1	Tue Jun 29 22:07:53 2010	(r209599)
+++ head/bin/sh/sh.1	Tue Jun 29 22:37:45 2010	(r209600)
@@ -32,7 +32,7 @@
 .\"	from: @(#)sh.1	8.6 (Berkeley) 5/4/95
 .\" $FreeBSD$
 .\"
-.Dd May 24, 2010
+.Dd June 29, 2010
 .Dt SH 1
 .Os
 .Sh NAME
@@ -1106,6 +1106,10 @@ command executed from the current shell.
 For a
 pipeline, the process ID is that of the last command in the
 pipeline.
+If this parameter is referenced, the shell will remember
+the process ID and its exit status until the
+.Ic wait
+built-in command reports completion of the process.
 .It Li $0
 (zero) Expands to the name of the shell or shell script.
 .El


More information about the svn-src-all mailing list