Linux之进程的等待与其内核实现解析
进程通过fork产生子进程,进程也会死亡,进程退出的时候将会进行内核清理,释放所有进程的资源,资源包括:内存资源,文件资源,信号量资源,共享内存资源,或者引用计数减一,或者彻底释放。
include <sys/wait.h>pid_t wait(int *status);
while((childPid = wait(NULL)) != -1)continue;if(errno !=ECHILD)errExit("wait");
pid_t r_wait(int *stat_loc){int retval;while(((retval = wait(stat_loc)) == -1 &&(errno == EINTR))//被信号打断 EINTR;return retval;}while((childPid = r_wait(NULL)) != -1)continue;If(errno != ECHILD){/*some error happened*/}
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,int, options, struct rusage __user *, ru){struct wait_opts wo;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) { //等待所有子进程中,进程组ID与pid绝对值相等的所有子进程type = PIDTYPE_PGID;pid = find_get_pid(-upid);} else if (upid == 0) { //表示等待与调用进程同一个进程组的任意子进程type = PIDTYPE_PGID;pid = get_task_pid(current, PIDTYPE_PGID);} else /* upid > 0 */ { //等待pid值的进程type = PIDTYPE_PID;pid = find_get_pid(upid);}wo.wo_type = type;wo.wo_pid = pid;wo.wo_flags = options | WEXITED;wo.wo_info = NULL;wo.wo_stat = stat_addr;wo.wo_rusage = ru;ret = do_wait(&wo);put_pid(pid);/* avoid REGPARM breakage on x86: */asmlinkage_protect(4, ret, upid, stat_addr, options, ru);return ret;}
static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk){struct task_struct *p;list_for_each_entry(p, &tsk->children, sibling) {/*遍历进程所有的子进程*/int ret = wait_consider_task(wo, 0, p);if (ret)return ret;}return 0;}
/* 当waitpid的第一个参数为-1时, wo->wo_type 赋值为PIDTYPE_MAX* 其他三种情况task_pid_type(p, wo->wo_type)== wo->wo_pid检验* 或者检查pid是否相等, 或者检查进程组ID是否等于指定值*/static int eligible_pid(struct wait_opts *wo, struct task_struct *p){return wo->wo_type == PIDTYPE_MAX ||task_pid_type(p, wo->wo_type) == wo->wo_pid; //其他三种情况task_pid_type(p, wo->wo_type)== wo->wo_pid检验}
void print_wait_exit(int status){printf("status = %d\n",status);if(WIFEXITED(status)){printf("normal termination,exit status = %d\n",WEXITSTATUS(status));}else if(WIFSIGNALED(status)){printf("abnormal termination,signal number =%d%s\n",WTERMSIG(status),#ifdef WCOREDUMPWCOREDUMP(status)?"core file generated" : "");#else"");#endif}}
static void forget_original_parent(struct task_struct *father){struct task_struct *p, *n, *reaper;LIST_HEAD(dead_children);write_lock_irq(&tasklist_lock);/** Note that exit_ptrace() and find_new_reaper() might* drop tasklist_lock and reacquire it.*/exit_ptrace(father);reaper = find_new_reaper(father);list_for_each_entry_safe(p, n, &father->children, sibling) {struct task_struct *t = p;do {t->real_parent = reaper;if (t->parent == father) {BUG_ON(t->ptrace);t->parent = t->real_parent;}/*内核提供了机制, 允许父进程退出时向子进程发送信号*/if (t->pdeath_signal)group_send_sig_info(t->pdeath_signal,SEND_SIG_NOINFO, t);} while_each_thread(p, t);reparent_leader(father, p, &dead_children);}write_unlock_irq(&tasklist_lock);BUG_ON(!list_empty(&father->children));list_for_each_entry_safe(p, n, &dead_children, sibling) {list_del_init(&p->sibling);release_task(p);}}
if (t->pdeath_signal)group_send_sig_info(t->pdeath_signal,SEND_SIG_NOINFO, t);
else if (thread_group_leader(tsk)) {/*线程组组长只有在全部线程都已退出的情况下,*才能调用do_notify_parent通知父进程*/autoreap = thread_group_empty(tsk) && //必须全部退出才会do_notify_parent(tsk, tsk->exit_signal);} else {/*如果是线程组的非组长线程, 可以立即调用release_task,*释放残余的资源, 因为通知父进程这件事和它没有关系*/autoreap = true;}
leader = p->group_leader;//不是主线程 自己是最后一个线程 主线程除以僵尸状态if (leader != p && thread_group_empty(leader) && leader->exit_state == EXIT_ZOMBIE) {zap_leader = do_notify_parent(leader, leader->exit_signal);//像父进程发送信号函数if (zap_leader)leader->exit_state = EXIT_DEAD;}
//子进程的主要线程pcb,退出信号bool do_notify_parent(struct task_struct *tsk, int sig){struct siginfo info;unsigned long flags;struct sighand_struct *psig;bool autoreap = false;BUG_ON(sig == -1);/* do_notify_parent_cldstop should have been called instead. */BUG_ON(task_is_stopped_or_traced(tsk));BUG_ON(!tsk->ptrace &&(tsk->group_leader != tsk || !thread_group_empty(tsk)));if (sig != SIGCHLD) {/** This is only possible if parent == real_parent.* Check if it has changed security domain.*/if (tsk->parent_exec_id != tsk->parent->self_exec_id)sig = SIGCHLD;}info.si_signo = sig;info.si_errno = 0;rcu_read_lock();info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);info.si_uid = __task_cred(tsk)->uid;rcu_read_unlock();info.si_utime = cputime_to_clock_t(cputime_add(tsk->utime,tsk->signal->utime));info.si_stime = cputime_to_clock_t(cputime_add(tsk->stime,tsk->signal->stime));info.si_status = tsk->exit_code & 0x7f;if (tsk->exit_code & 0x80)info.si_code = CLD_DUMPED;else if (tsk->exit_code & 0x7f)info.si_code = CLD_KILLED;else {info.si_code = CLD_EXITED;info.si_status = tsk->exit_code >> 8;}psig = tsk->parent->sighand;spin_lock_irqsave(&psig->siglock, flags)//是SIGCHLD信号 但父进程的信号处理函数设置为SIG_IGN或者flag设置为SA_NOCLDWAIT位if (!tsk->ptrace && sig == SIGCHLD &&(psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||(psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) {autoreap = true;//设置为true,表示父进程不关心自己的退出信息,将会调用release_task函数,释放残余的资源,自行了断,子进程也就不会进入僵尸状态了。if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN)sig = 0;}/*子进程向父进程发送信号*/if (valid_signal(sig) && sig)__group_send_sig_info(sig, &info, tsk->parent);/* 子进程尝试唤醒父进程, 如果父进程正在等待其终止 */__wake_up_parent(tsk, tsk->parent);spin_unlock_irqrestore(&psig->siglock, flags);return autoreap;}
while(waitpid(-1,&status,WNOHANG) > 0){/*此处处理返回信息*/continue;}
void __wake_up_parent(struct task_struct *p, struct task_struct *parent){ //等待队列头__wake_up_sync_key(&parent->signal->wait_chldexit,TASK_INTERRUPTIBLE, 1, p);}
static long do_wait(struct wait_opts *wo){struct task_struct *tsk;int retval;trace_sched_process_wait(wo->wo_pid);/*挂入等待队列*/init_waitqueue_func_entry(&wo->child_wait, child_wait_callback);wo->child_wait.private = current;add_wait_queue(¤t->signal->wait_chldexit, &wo->child_wait);repeat:/**/wo->notask_error = -ECHILD;if ((wo->wo_type < PIDTYPE_MAX) &&(!wo->wo_pid || hlist_empty(&wo->wo_pid->tasks[wo->wo_type])))goto notask;set_current_state(TASK_INTERRUPTIBLE);//父进程设置自己为此状态read_lock(&tasklist_lock);tsk = current;do {retval = do_wait_thread(wo, tsk);if (retval)goto end;retval = ptrace_do_wait(wo, tsk);if (retval)goto end;if (wo->wo_flags & __WNOTHREAD)break;} while_each_thread(current, tsk);read_unlock(&tasklist_lock);/*找了一圈, 没有找到满足等待条件的的子进程, 下一步的行为将取决于WNOHANG标志位*如果将WNOHANG标志位置位, 则表示不等了, 直接退出,*如果没有置位, 则让出CPU, 醒来后继续再找一圈*/notask:retval = wo->notask_error;if (!retval && !(wo->wo_flags & WNOHANG)) {retval = -ERESTARTSYS;if (!signal_pending(current)) {schedule();goto repeat;}}end:__set_current_state(TASK_RUNNING);//找到了满足条件的子进程设置为此状态remove_wait_queue(¤t->signal->wait_chldexit, &wo->child_wait);return retval;}tsk = current;
Linux之进程的等待与其内核实现解析的更多相关文章
- linux创建进程和等待进程退出
在WIN32下,在一个进程里我们可以使用CreateProcess()创建一个进程,然后通过调用WaitForSingleObect(), WaitForMultipleObject()等待进程退出. ...
- linux常见进程与内核线程
发现大量jdb2进程占用io资源.jdb2进程是一个文件系统的写journal的进程 kthreadd:这种内核线程只有一个,它的作用是管理调度其它的内核线程.它在内核初始化的时候被创建,会循环运行一 ...
- 《Linux内核设计与实现》读书笔记(三)- Linux的进程
进程是所有操作系统的核心概念,同样在linux上也不例外. 主要内容: 进程和线程 进程的生命周期 进程的创建 进程的终止 1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理 ...
- Linux计算机进程地址空间与内核装载ELF
本文基于Linux™系统对进程创建与加载进行分析,文中实现了Linux库函数fork.exec,剖析内核态执行过程,并进一步展示进程创建过程中进程控制块字段变化信息及ELF文件加载过程. 一.初识Li ...
- linux管理进程的链表
linux2.6.11的内核中,为了方便管理linux的进程,主要建了5种linux链表.每个链表节点之间的互联有两种方式,一种是hash节点之间的互联,通过hlist_node的数据结构来实现:另一 ...
- 创建守护进程步骤与setsid() -- linux deamon进程
原创:http://www.cnblogs.com/mickole/p/3188321.html 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且 ...
- Linux守护进程的编程实现(转)
http://blog.csdn.net/zg_hover/article/details/2553321 http://blog.csdn.net/kongdefei5000/article/det ...
- linux之间进程通信
进程间通信方式: 同主机进程间数据交换机制: pipe(无名管道) / fifo(有名管道)/ message queue(消息队列)和共享内存. 必备基础: f ...
- [Linux] 孤儿进程与僵尸进程[总结]
转载: http://www.cnblogs.com/Anker/p/3271773.html 1.前言 之前在看<unix环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两 ...
随机推荐
- HomeKit开发(一)
需求:因为公司的生产硬件需要进入苹果的HomeKit市场,因此需要一款供苹果审核的APP(功能:苹果的家庭软件的功能+公司的硬件支持的功能特有功能)需求驱动开发:最近做了一款苹果HomeKit的软件 ...
- dSYM文件
来到新公司后,前段时间就一直在忙,前不久 项目 终于成功发布上线了,最近就在给项目做优化,并排除一些线上软件的 bug,因为项目中使用了友盟统计,所以在友盟给出的错误信息统计中能比较方便的找出客户端异 ...
- runtime实践之Method Swizzling
利用 Objective-C 的 Runtime 特性,我们可以给语言做扩展,帮助解决项目开发中的一些设计和技术问题.这一篇,我们来探索一些利用 Objective-C Runtime 的黑色技巧.这 ...
- Qt的由来和发展
一.Qt的由来 Haavard Nord 和Eirik Chambe-Eng于1991年开始开发"Qt",1994年3月4日创立公司,早名为Quasar Technologies, ...
- vue 配置多页面应用
前言: 本文基于vue 2.5.2, webpack 3.6.0(配置多页面原理类似,实现方法各有千秋,可根据需要进行定制化) vue 是单页面应用.但是在做大型项目时,单页面往往无法满足我们的需求, ...
- laravel中使用PHPQuery实现网页采集
由于没有PHPQuery的composer包安装所以需要我们手动在我们的laravel项目中安装加载PHPQuery,这里需要设置laravel的autoload->class map. 1.首 ...
- source insight
关于source inlight的版本 http://www.camnpr.com/archives/559.html 最新版本 http://www.sourceinsight.com/upda ...
- SPOJ QTREE4 - Query on a tree IV 树分治
题意: 给出一棵边带权的树,初始树上所有节点都是白色. 有两种操作: C x,改变节点x的颜色,即白变黑,黑变白 A,询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为0). 分析: ...
- luogu1242 新汉诺塔
就是一步一步把大的往目标地放. #include <iostream> #include <cstdio> using namespace std; int fro[55], ...
- 03_HibernateSessionFactory源码分析
文章导读: 讲解了一个线程为什么要使用同一个connection, 我们分析了HiberatenSessionFactory的实现机制, 然后根据Hibernate的写法重构了我们的代码. 最后测试可 ...