六: wait4 ()系统调用
在父进程中,用wait4()可以获得子进程的退出状态,并且防止在父进程退出前,子进程退出造成僵死 状态。这是我们这节分析的最后一个小节了。
关于wait4()在用户空间的调用方式可以自行参考相关资料,在这里只是讨论内核对这个系统调用的实 现过程。
Wait4()的系统调用入口为sys_wait4().代码如下所示:
asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, int options, struct rusage __user *ru) { long ret; //options的标志为须为WNOHANG…__WALL的组合,否则会出错 //相关标志的作用在do_wait()中再进行分析 if (options & ~(WNOHANG|WUNTRACED|WCONTINUED| __WNOTHREAD|__WCLONE|__WALL)) return -EINVAL; ret = do_wait(pid, options | WEXITED, NULL, stat_addr, ru); /* avoid REGPARM breakage on x86: */ prevent_tail_call(ret); return ret; }
do_wait()是其中的核心处理函数。代码如下:
static long do_wait(pid_t pid, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru) { //初始化一个等待队列 DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; int flag, retval; int allowed, denied; //将当前进程加入等待队列,子进程退出给父进程发送信号会wake up些等待队列 add_wait_queue(¤t->signal->wait_chldexit,&wait); repeat: flag = 0; allowed = denied = 0; //设置进程状态为TASK_INTERRUPTIBLE.下次调度必须要等到子进程唤醒才可以了 current->state = TASK_INTERRUPTIBLE; read_lock(&tasklist_lock); tsk = current; do { struct task_struct *p; struct list_head *_p; int ret; //遍历进程下的子进程 list_for_each(_p,&tsk->children) { p = list_entry(_p, struct task_struct, sibling); //判断是否是我们要wait 的子进程 ret = eligible_child(pid, options, p); if (!ret) continue; if (unlikely(ret < 0)) { denied = ret; continue; } allowed = 1; switch (p->state) { //子进程为TASK_TRACED.即处于跟踪状态。则取子进程的相关信息 case TASK_TRACED: flag = 1; //判断是否是被父进程跟踪的子进程 //如果是则返回1..不是返回0 if (!my_ptrace_child(p)) continue; /*FALLTHROUGH*/ case TASK_STOPPED: flag = 1; //WUNTRACED:子进程是停止的,也马上返回 //没有定义WUNTRACED 参数.继续遍历子进程 /*从此看出.生父进程是不会处理STOP状态的子进程的.只有 发起跟踪的进程才会 */ if (!(options & WUNTRACED) && !my_ptrace_child(p)) continue; //WNOWAIT:不会将zombie子进程的退出状态撤销 //下次调用wait系列函数的时候还可以继续获得这个退出状态 retval = wait_task_stopped(p, ret == 2, (options & WNOWAIT), infop, stat_addr, ru); if (retval == -EAGAIN) goto repeat; if (retval != 0) /* He released the lock. */ goto end; break; default: // case EXIT_DEAD: //不需要处理DEAD状态 if (p->exit_state == EXIT_DEAD) continue; // case EXIT_ZOMBIE: //子进程为僵尸状态 if (p->exit_state == EXIT_ZOMBIE) { if (ret == 2) goto check_continued; if (!likely(options & WEXITED)) continue; retval = wait_task_zombie( p, (options & WNOWAIT), infop, stat_addr, ru); /* He released the lock. */ if (retval != 0) goto end; break; } check_continued: /* * It's running now, so it might later * exit, stop, or stop and then continue. */ flag = 1; //WCONTINUED:报告任何继续运行的指定进程号的子进程的状态 if (!unlikely(options & WCONTINUED)) continue; //取进程的相关状态 retval = wait_task_continued( p, (options & WNOWAIT), infop, stat_addr, ru); if (retval != 0) /* He released the lock. */ goto end; break; } } //遍历被跟踪出去的子进程 //从这里可以看出.如果一个子进程被跟踪出去了.那么子进程的退出 //操作并不是由生父进程进行了 if (!flag) { list_for_each(_p, &tsk->ptrace_children) { p = list_entry(_p, struct task_struct, ptrace_list); if (!eligible_child(pid, options, p)) continue; flag = 1; break; } } if (options & __WNOTHREAD) break; //也有可能是进程中的线程在wait其fork出来的子进程 tsk = next_thread(tsk); BUG_ON(tsk->signal != current->signal); } while (tsk != current); // read_unlock(&tasklist_lock); if (flag) { retval = 0; //如果定义了WHNOHANG:马上退出 if (options & WNOHANG) goto end; retval = -ERESTARTSYS; if (signal_pending(current)) goto end; schedule(); goto repeat; } retval = -ECHILD; if (unlikely(denied) && !allowed) retval = denied; 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; }
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索进程
, 状态
, continue
, Wait()
, Options
, fallthrough
, current
RETVAL
wait系统调用、linux 查看系统进程、linux 进程被系统kill、linux系统进程类型、linux系统进程管理,以便于您获取更多的相关知识。
时间: 2024-10-30 00:56:26