summaryrefslogtreecommitdiffstats
path: root/bin/sh/jobs.c
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2010-06-29 22:37:45 +0000
committerjilles <jilles@FreeBSD.org>2010-06-29 22:37:45 +0000
commit8fcbe1caf89944ec27f00e1265b29374fd3e05de (patch)
treefec6cbce2d09b1054b1fc671f8093cc73e9c8694 /bin/sh/jobs.c
parent49f38732fcf51babc8f4ba652aa99ac2a78eeb4b (diff)
downloadFreeBSD-src-8fcbe1caf89944ec27f00e1265b29374fd3e05de.zip
FreeBSD-src-8fcbe1caf89944ec27f00e1265b29374fd3e05de.tar.gz
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
Diffstat (limited to 'bin/sh/jobs.c')
-rw-r--r--bin/sh/jobs.c61
1 files changed, 56 insertions, 5 deletions
diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c
index 29939d1..f55b637 100644
--- a/bin/sh/jobs.c
+++ b/bin/sh/jobs.c
@@ -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 nprocs)
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 nprocs)
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, int mode)
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.
OpenPOWER on IntegriCloud