为什么调用fork后需要调用wait

就拿linux0.11源码分析,从进程2的创建与销毁举例子。在这里贴出的代码看不懂的不要紧,我会尽量把流程说清楚,看的懂的那是最好,程序员的语言不就是代码吗~ 😂

linux内核被加载到内存后,就会执行main.c中的main函数,创建进程0,进程0创建进程1,进程1创建进程2

void main(void)     
{           
    ...
    if (!fork()) {      
        init();  // 在新建的子进程(任务1)中执行。
    }
    ...
}

fokk后就会创建进程1,在子进程中(进程1)fork返回0,为什么返回0,在这里说的比较清楚了
init()由进程1开始执行。

void init(void)
{
    ...
    if (!(pid=fork())) {
       ...进程2执行块···
    }
        ...进程1执行块···
    if (pid>0)
        while (pid != wait(&i))

        ...
}

进程1里面又要执行fork,创建进程2,pid等于0代表的是子进程,大于0代表父进程,所以父进程(进程1)调用wait

wait的定义在lib/wait.c文件中,

#define __LIBRARY__
#include <unistd.h>
#include <sys/wait.h>

_syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
pid_t wait(int * wait_stat)
{
    return waitpid(-1,wait_stat,0);
}

_syscall3声明在unistd.h头文件中。调用wait最终调用的是sys_waitpid

#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
    return (type) __res; \
errno=-__res; \
return -1; \
}

sys_waitpid找到进程后,就会把进程的状态设置为中断等待态,并且重新调度schedule()
进程调度发现进程的状态时中断等待状态,就不会执行该进程,需要等待下一次调度。

int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
    int flag, code;             // flag标志用于后面表示所选出的子进程处于就绪或睡眠态。
    struct task_struct ** p;

    verify_area(stat_addr,4);
repeat:
    flag=0;
    
    ...省略部分代码···
    
    // 在上面对任务数组扫描结束后,如果flag被置位,说明有符合等待要求的子进程并没有处于退出或
    // 僵死状态。如果此时已设置WNOHANG选项(表示若没有子进程处于退出或终止态就立刻返回),就
    // 立刻返回0,退出。否则把当前进程置为可中断等待状态并重新执行调度。当又开始执行本进程时,
    // 如果本进程没有收到除SIGCHLD以外的信号,则还是重复处理。否则,返回出错码‘中断系统调用’
    // 并退出。针对这个出错号用户程序应该再继续调用本函数等待子进程。
    if (flag) {
        if (options & WNOHANG)                  // options = WNOHANG,则立刻返回。
            return 0;
        current->state=TASK_INTERRUPTIBLE;      // 置当前进程为可中断等待态
        schedule();                             // 重新调度。
        if (!(current->signal &= ~(1<<(SIGCHLD-1))))
            goto repeat;
        else
            return -EINTR;                      // 返回出错码(中断的系统调用)
    }
    // 若没有找到符合要求的子进程,则返回出错码(子进程不存在)。
    return -ECHILD;
}

调用wait后,进程1就不再CPU上执行了,此时进程2的状态时可运行状态,进程调度后,进程2开始执行,进程2做完自己的事后,就会执行_exit方法,该方法对应就是 sys_exit

int sys_exit(int error_code)
{
    return do_exit((error_code&0xff)<<8);
}

do_exit主要事释放进程占用的页,然后把自己的状态改为TASK_ZOMBIE僵死状态,
tell_father(current->father);通知父进程,给父进程发送信号task[i]->signal |= (1<<(SIGCHLD-1));,不是说给父进程发信号,父进程就会醒来,需要等待下一次进程调度schedule();

int do_exit(long code)
{
    ...释放进程自己的页等数据···
    current->state = TASK_ZOMBIE;
    current->exit_code = code;
    // 通知父进程,也即向父进程发送信号SIGCHLD - 子进程将停止或终止。
    tell_father(current->father);
    schedule();  // 重新调度进程运行,以让父进程处理僵死其他的善后事宜。
 
    return (-1);
}
static void tell_father(int pid)
{
    int i;

    if (pid)
        // 扫描进城数组表,寻找指定进程pid,并向其发送子进程将停止或终止信号SIGCHLD。
        for (i=0;i<NR_TASKS;i++) {
            if (!task[i])
                continue;
            if (task[i]->pid != pid)
                continue;
            task[i]->signal |= (1<<(SIGCHLD-1));
            return;
        }
/* if we don't find any fathers, we just release ourselves */
/* This is not really OK. Must change it to make father 1 */
    printk("BAD BAD - no father found\n\r");
    release(current);  // 如果没有找到父进程,则自己释放
}

到这里子进程释放了自己的占用的内存页数据,但是在进程表里还占用进程结构,为什么不释放完呢,因为父进程还需要知道子进程的返回值,该返回值就存在进程结构体里,子进程已经是僵死状态了,这再也不会运行了,等下一次进程调度后,父进程发现收到SIGCHLD信号,并且是可中断等待状态,父进程开始苏醒,接着上次wait代码处执行,也就是上面提到的sys_waitpid,该方法找到子进程是TASK_ZOMBIE的进程,释放子进程占用的进程项。

如果父进程不调用wait,子进程结束后,父进程不知道子进程有没有死,就没有办法释放子进程,虽然子进程释放了自己占用内存页,但是没有办法释放占用的进程结构体,子进程就会变成僵尸进程。

case TASK_ZOMBIE:
    current->cutime += (*p)->utime;
    current->cstime += (*p)->stime;
    flag = (*p)->pid;                   // 临时保存子进程pid
    code = (*p)->exit_code;             // 取子进程的退出码
    release(*p);                        // 释放该子进程
    put_fs_long(code,stat_addr);        // 置状态信息为退出码值
    return flag;                        // 返回子进程的pid
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容