📄 exit.c
字号:
status &= 0x7f; } retval = put_user((short)why, &infop->si_code); if (!retval) retval = put_user(status, &infop->si_status); } if (!retval && infop) retval = put_user(pid, &infop->si_pid); if (!retval && infop) retval = put_user(p->uid, &infop->si_uid); if (!retval) retval = pid; if (traced) { write_lock_irq(&tasklist_lock); /* We dropped tasklist, ptracer could die and untrace */ ptrace_unlink(p); /* * If this is not a detached task, notify the parent. * If it's still not detached after that, don't release * it now. */ if (!task_detached(p)) { do_notify_parent(p, p->exit_signal); if (!task_detached(p)) { p->exit_state = EXIT_ZOMBIE; p = NULL; } } write_unlock_irq(&tasklist_lock); } if (p != NULL) release_task(p); return retval;}/* * Handle sys_wait4 work for one task in state TASK_STOPPED. We hold * read_lock(&tasklist_lock) on entry. If we return zero, we still hold * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */static int wait_task_stopped(int ptrace, struct task_struct *p, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru){ int retval, exit_code, why; uid_t uid = 0; /* unneeded, required by compiler */ pid_t pid; if (!(options & WUNTRACED)) return 0; exit_code = 0; spin_lock_irq(&p->sighand->siglock); if (unlikely(!task_is_stopped_or_traced(p))) goto unlock_sig; if (!ptrace && p->signal->group_stop_count > 0) /* * A group stop is in progress and this is the group leader. * We won't report until all threads have stopped. */ goto unlock_sig; exit_code = p->exit_code; if (!exit_code) goto unlock_sig; if (!unlikely(options & WNOWAIT)) p->exit_code = 0; uid = p->uid;unlock_sig: spin_unlock_irq(&p->sighand->siglock); if (!exit_code) return 0; /* * Now we are pretty sure this task is interesting. * Make sure it doesn't get reaped out from under us while we * give up the lock and then examine it below. We don't want to * keep holding onto the tasklist_lock while we call getrusage and * possibly take page faults for user memory. */ get_task_struct(p); pid = task_pid_vnr(p); why = ptrace ? CLD_TRAPPED : CLD_STOPPED; read_unlock(&tasklist_lock); if (unlikely(options & WNOWAIT)) return wait_noreap_copyout(p, pid, uid, why, exit_code, infop, ru); retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; if (!retval && stat_addr) retval = put_user((exit_code << 8) | 0x7f, stat_addr); if (!retval && infop) retval = put_user(SIGCHLD, &infop->si_signo); if (!retval && infop) retval = put_user(0, &infop->si_errno); if (!retval && infop) retval = put_user((short)why, &infop->si_code); if (!retval && infop) retval = put_user(exit_code, &infop->si_status); if (!retval && infop) retval = put_user(pid, &infop->si_pid); if (!retval && infop) retval = put_user(uid, &infop->si_uid); if (!retval) retval = pid; put_task_struct(p); BUG_ON(!retval); return retval;}/* * Handle do_wait work for one task in a live, non-stopped state. * read_lock(&tasklist_lock) on entry. If we return zero, we still hold * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */static int wait_task_continued(struct task_struct *p, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru){ int retval; pid_t pid; uid_t uid; if (!unlikely(options & WCONTINUED)) return 0; if (!(p->signal->flags & SIGNAL_STOP_CONTINUED)) return 0; spin_lock_irq(&p->sighand->siglock); /* Re-check with the lock held. */ if (!(p->signal->flags & SIGNAL_STOP_CONTINUED)) { spin_unlock_irq(&p->sighand->siglock); return 0; } if (!unlikely(options & WNOWAIT)) p->signal->flags &= ~SIGNAL_STOP_CONTINUED; spin_unlock_irq(&p->sighand->siglock); pid = task_pid_vnr(p); uid = p->uid; get_task_struct(p); read_unlock(&tasklist_lock); if (!infop) { retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; put_task_struct(p); if (!retval && stat_addr) retval = put_user(0xffff, stat_addr); if (!retval) retval = pid; } else { retval = wait_noreap_copyout(p, pid, uid, CLD_CONTINUED, SIGCONT, infop, ru); BUG_ON(retval == 0); } return retval;}/* * Consider @p for a wait by @parent. * * -ECHILD should be in *@notask_error before the first call. * Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns zero if the search for a child should continue; * then *@notask_error is 0 if @p is an eligible child, * or another error from security_task_wait(), or still -ECHILD. */static int wait_consider_task(struct task_struct *parent, int ptrace, struct task_struct *p, int *notask_error, enum pid_type type, struct pid *pid, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru){ int ret = eligible_child(type, pid, options, p); if (!ret) return ret; if (unlikely(ret < 0)) { /* * If we have not yet seen any eligible child, * then let this error code replace -ECHILD. * A permission error will give the user a clue * to look for security policy problems, rather * than for mysterious wait bugs. */ if (*notask_error) *notask_error = ret; } if (likely(!ptrace) && unlikely(p->ptrace)) { /* * This child is hidden by ptrace. * We aren't allowed to see it now, but eventually we will. */ *notask_error = 0; return 0; } if (p->exit_state == EXIT_DEAD) return 0; /* * We don't reap group leaders with subthreads. */ if (p->exit_state == EXIT_ZOMBIE && !delay_group_leader(p)) return wait_task_zombie(p, options, infop, stat_addr, ru); /* * It's stopped or running now, so it might * later continue, exit, or stop again. */ *notask_error = 0; if (task_is_stopped_or_traced(p)) return wait_task_stopped(ptrace, p, options, infop, stat_addr, ru); return wait_task_continued(p, options, infop, stat_addr, ru);}/* * Do the work of do_wait() for one thread in the group, @tsk. * * -ECHILD should be in *@notask_error before the first call. * Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns zero if the search for a child should continue; then * *@notask_error is 0 if there were any eligible children, * or another error from security_task_wait(), or still -ECHILD. */static int do_wait_thread(struct task_struct *tsk, int *notask_error, enum pid_type type, struct pid *pid, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru){ struct task_struct *p; list_for_each_entry(p, &tsk->children, sibling) { /* * Do not consider detached threads. */ if (!task_detached(p)) { int ret = wait_consider_task(tsk, 0, p, notask_error, type, pid, options, infop, stat_addr, ru); if (ret) return ret; } } return 0;}static int ptrace_do_wait(struct task_struct *tsk, int *notask_error, enum pid_type type, struct pid *pid, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru){ struct task_struct *p; /* * Traditionally we see ptrace'd stopped tasks regardless of options. */ options |= WUNTRACED; list_for_each_entry(p, &tsk->ptraced, ptrace_entry) { int ret = wait_consider_task(tsk, 1, p, notask_error, type, pid, options, infop, stat_addr, ru); if (ret) return ret; } return 0;}static long do_wait(enum pid_type type, struct pid *pid, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru){ DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; int retval; add_wait_queue(¤t->signal->wait_chldexit,&wait);repeat: /* * If there is nothing that can match our critiera just get out. * We will clear @retval to zero if we see any child that might later * match our criteria, even if we are not able to reap it yet. */ retval = -ECHILD; if ((type < PIDTYPE_MAX) && (!pid || hlist_empty(&pid->tasks[type]))) goto end; current->state = TASK_INTERRUPTIBLE; read_lock(&tasklist_lock); tsk = current; do { int tsk_result = do_wait_thread(tsk, &retval, type, pid, options, infop, stat_addr, ru); if (!tsk_result) tsk_result = ptrace_do_wait(tsk, &retval, type, pid, options, infop, stat_addr, ru); if (tsk_result) { /* * tasklist_lock is unlocked and we have a final result. */ retval = tsk_result; goto end; } if (options & __WNOTHREAD) break; tsk = next_thread(tsk); BUG_ON(tsk->signal != current->signal); } while (tsk != current); read_unlock(&tasklist_lock); if (!retval && !(options & WNOHANG)) { retval = -ERESTARTSYS; if (!signal_pending(current)) { schedule(); goto repeat; } }end: current->state = TASK_RUNNING; remove_wait_queue(¤t->signal->wait_chldexit,&wait); if (infop) { if (retval > 0) retval = 0; else { /* * For a WNOHANG return, clear out all the fields * we would set so the user can easily tell the * difference. */ if (!retval) retval = put_user(0, &infop->si_signo); if (!retval) retval = put_user(0, &infop->si_errno); if (!retval) retval = put_user(0, &infop->si_code); if (!retval) retval = put_user(0, &infop->si_pid); if (!retval) retval = put_user(0, &infop->si_uid); if (!retval) retval = put_user(0, &infop->si_status); } } return retval;}asmlinkage long sys_waitid(int which, pid_t upid, struct siginfo __user *infop, int options, struct rusage __user *ru){ struct pid *pid = NULL; enum pid_type type; long ret; if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED)) return -EINVAL; if (!(options & (WEXITED|WSTOPPED|WCONTINUED))) return -EINVAL; switch (which) { case P_ALL: type = PIDTYPE_MAX; break; case P_PID: type = PIDTYPE_PID; if (upid <= 0) return -EINVAL; break; case P_PGID: type = PIDTYPE_PGID; if (upid <= 0) return -EINVAL; break; default: return -EINVAL; } if (type < PIDTYPE_MAX) pid = find_get_pid(upid); ret = do_wait(type, pid, options, infop, NULL, ru); put_pid(pid); /* avoid REGPARM breakage on x86: */ asmlinkage_protect(5, ret, which, upid, infop, options, ru); return ret;}asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr, int options, struct rusage __user *ru){ struct pid *pid = NULL; enum pid_type type; long ret; if (options & ~(WNOHANG|WUNTRACED|WCONTINUED| __WNOTHREAD|__WCLONE|__WALL)) return -EINVAL; if (upid == -1) type = PIDTYPE_MAX; else if (upid < 0) { type = PIDTYPE_PGID; pid = find_get_pid(-upid); } else if (upid == 0) { type = PIDTYPE_PGID; pid = get_pid(task_pgrp(current)); } else /* upid > 0 */ { type = PIDTYPE_PID; pid = find_get_pid(upid); } ret = do_wait(type, pid, options | WEXITED, NULL, stat_addr, ru); put_pid(pid); /* avoid REGPARM breakage on x86: */ asmlinkage_protect(4, ret, upid, stat_addr, options, ru); return ret;}#ifdef __ARCH_WANT_SYS_WAITPID/* * sys_waitpid() remains for compatibility. waitpid() should be * implemented by calling sys_wait4() from libc.a. */asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options){ return sys_wait4(pid, stat_addr, options, NULL);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -