Prevent zombie processes after disowned child procs exit

Closes #5346.
This commit is contained in:
Mahmoud Al-Qudsi 2018-11-18 15:18:18 -06:00
parent 58b29fb5d1
commit 8730b482a7
3 changed files with 25 additions and 2 deletions

View File

@ -31,8 +31,13 @@ static int disown_job(const wchar_t *cmd, parser_t &parser, io_streams_t &stream
streams.err.append_format(fmt, cmd, j->job_id, j->command_wcstr());
}
if (parser.job_remove(j)) return STATUS_CMD_OK;
return STATUS_CMD_ERROR;
pid_t pgid = j->pgid;
if (!parser.job_remove(j)) {
return STATUS_CMD_ERROR;
}
add_disowned_pgid(pgid);
return STATUS_CMD_OK;
}
/// Builtin for removing jobs from the job list.

View File

@ -349,6 +349,14 @@ io_chain_t job_t::all_io_redirections() const {
typedef unsigned int process_generation_count_t;
/// A list of pids/pgids that have been disowned. They are kept around until either they exit or
/// we exit. Poll these from time-to-time to prevent zombie processes from happening (#5342).
static std::vector<pid_t> s_disowned_pids;
void add_disowned_pgid(pid_t pgid) {
s_disowned_pids.push_back(pgid * -1);
}
/// A static value tracking how many SIGCHLDs we have seen, which is used in a heurstic to
/// determine if we should call waitpid() at all in `process_mark_finished_children`.
static volatile process_generation_count_t s_sigchld_generation_cnt = 0;
@ -496,6 +504,13 @@ static bool process_mark_finished_children(bool block_on_fg) {
}
}
// Poll disowned processes/process groups, but do nothing with the result. Only used to avoid
// zombie processes. Entries have already be converted to negative for process groups.
int status;
s_disowned_pids.erase(std::remove_if(s_disowned_pids.begin(), s_disowned_pids.end(),
[&status](pid_t pid) { return waitpid(pid, &status, WNOHANG) > 0; }),
s_disowned_pids.end());
// Yes, the below can be collapsed to a single line, but it's worth being explicit about it with
// the comments. Fret not, the compiler will optimize it. (It better!)
if (jobs_skipped) {

View File

@ -408,6 +408,9 @@ bool terminal_give_to_job(const job_t *j, bool restore_attrs);
/// Returns the pid to restore after running the builtin, or -1 if there is no pid to restore.
pid_t terminal_acquire_before_builtin(int job_pgid);
/// Add a pid to the list of pids we wait on even though they are not associated with any jobs.
/// Used to avoid zombie processes after disown.
void add_disowned_pgid(pid_t pgid);
/// 0 should not be used; although it is not a valid PGID in userspace,
/// the Linux kernel will use it for kernel processes.