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环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两 ...
随机推荐
- 【转】关于“using namespace std”
对于一个存在着标准输入输出的C++控制台程序,一般会在#include <iostream>的下一行发现一句话,using namespace std.这句话其实就表示了所有的标准库函数都 ...
- 使用虚拟环境来管理python的包
1.背景 在开发python项目的过程中,我们会用到各种各样的包,我们使用pip来管理包,请看下图我们刚装好python解释器时已安装的包: 但是随着我们疯狂的使用pip install xxx后,系 ...
- Scrapy+Chromium+代理+selenium
上周说到scrapy的基本入门.这周来写写其中遇到的代理和js渲染的坑. js渲染 js是爬虫中毕竟麻烦处理的一块.通常的解决办法是通过抓包,然后查看request信息,接着捕获ajax返回的消息.但 ...
- 【laravel】【转发】laravel 导入导出excel文档
1.简介 Laravel Excel 在 Laravel 5 中集成 PHPOffice 套件中的 PHPExcel ,从而方便我们以优雅的.富有表现力的代码实现Excel/CSV文件的导入和 导出 ...
- yagmail 邮箱的使用
文章来源:GITHub:https://github.com/kootenpv/yagmail 安装 pip3 install yagmail pip3 install keyring 简单例子 im ...
- HMAC(Hash-based Message Authentication Code)实现原理
1.HMAC 概念 HMAC(Hash-based Message Authentication Code)基于 hash 的消息验证码,是 安全通信中必要的组成部件. 主要是 防止消息被篡改,和对称 ...
- poj-2524 ubiquitous religions(并查集)
Time limit5000 ms Memory limit65536 kB There are so many different religions in the world today that ...
- Java装饰者模式(Decorator)
一.定义 装饰模式的设计理念主要是以对客户端透明的方式动态扩展对象的功能,是继承关系的一个替代(继承会产生大量的子类,而且代码有冗余).装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展.装饰 ...
- Linux学习-额外(单一)核心模块编译
编译前注意事项 由于我们的核心原本就有提供很多的核心工具给硬件开发商来使用, 而硬件开发商也需要针对核心 所提供的功能来设计他们的驱动程序模块,因此, 我们如果想要自行使用硬件开发商所提供的模块 来进 ...
- Cacti安装脚本Server端+客户端
#!/bin/bash #auto make install LAMP+Cacti #by authors zhang #RRDtool define path variable R_FILES=rr ...