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 WCOREDUMP
WCOREDUMP(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环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两 ...
随机推荐
- Chunky Monkey-freecodecamp算法题目
Chunky Monkey(猴子吃香蕉, 分割数组) 要求 把一个数组arr按照指定的数组大小size分割成若干个数组块. 思路 利用size值和while语句确定切割数组的次数(定义temp将siz ...
- 三倍经验——bzoj3663、4660、4206 Crazy Rabbit/最大团
题目描述: 3663 4660 4206 题解: 第一眼:不成立的互相连边,然后用网络流求解无向图最小点覆盖! 好吧我不会. 正解: 每个点对应圆上的一段圆弧,长这样: 设对应圆弧$(l,r)$. 若 ...
- 使用MySQL yum源安装MySQL
#首先,将MySQL Yum存储库添加到系统的存储库列表中. #在https://dev.mysql.com/downloads/repo/yum/地址中,下载mysql yum repository ...
- Bootstrap 模态框 禁止点击空白关闭模态框事件
在模态框的div中加上 aria-hidden="true" data-backdrop="static" <div class="modal ...
- MySQL8.0.12安装及配置
一.下载 下载页面http://dev.mysql.com/downloads/mysql/ 选择系统平台后,点击download(根据系统选择64或32位) 二.配置 1.下载成功后,解压安装包到要 ...
- MHA
MHA 1. MHA简介 1.1 MHA工作原理总结为如下 1.2 MHA工具包介绍 2. 部署MHA 2.1 环境介绍 2.2 一主两从复制搭建 2.3 配置互信 2.4 下载MHA 2.5 安装M ...
- MDK editions for Nuvoton devices
10 Sep 2018 MDK editions for Nuvoton devices For users of Nuvoton devices, Keil® MDK increases its a ...
- Linux磁盘分区介绍
分区?我们不是已经在BIOS界面分区好了吗?如果领导给你一块磁盘,你怎么用呢?所以就有了分区工具(fdisk和parted),fdisk工具只针对小于2T磁盘分区,且是交互式的:parted很强大,通 ...
- 如何打造一个"逼格"的web前端项目
最近利用空余的时间(坐公交车看教程视频),重新了解了前后端分离,前端工程化等概念学习,思考如何打造一个“逼格”的web前端项目. 前端准备篇 前端代码规范:制定前端开发代码规范文档. PS:重中之中, ...
- Navicat Premium 连接Oracle登入时候报ORA-12638: 身份证明检索失败的解决办法
我的电脑是64位,oracle也是64位, plsql客户端是32位,oci连接的是32位 11.2版本: 用plsql 连接本地或远程数据库都没问题.在用 Navicat Premium 连本也没问 ...