判断for()作用域,区分那块属于哪个进程/多进程

来源:互联网 发布:淘宝网二级域名 编辑:程序博客网 时间:2024/04/29 16:17

刚开始接触进程(fork())的时候,比较难以理解的是如何实现多进程,也就是不知道如何来fork多个进程。通俗的想法可能是,我fork某个函数,就像我们创建了一个进程,这个函数就在这个进程上跑。这种想法比较贴切我们的观念,但是实际上,fork的机制并不是如此的“智能”,而是需要我们人为地“划定”界限,来规定那一块儿属于哪一个进程。这种描述可能还是不很贴切。

今天看了篇文章,写的不错,让我们能够比较直观地理解fork函数是如何发挥作用的,我呢就不班门弄斧了,这篇博文已经写的相当到位了。

来自:http://hi.baidu.com/aberlee/blog/item/777b99fcf13840f5fd037f0c.html


内容如下:

在一个程序里实现多进程,相对来讲,比在一个程序中实现多线程要更难理解,至少我在开始使用fork()的时候就很容易犯糊涂,所以一步一步来记录学习的过程。先来一段最简单的:
int main(void)
{
    printf("before fork(), pid = %d\n", getpid());
    fork();
    printf("after fork(), pid = %d\n", getpid());
    return 0;
}

    fork()的调用,就意味着从fork()的位置开始分叉,从一个进程变成了两个进程,原进程成为父进程,叉出来的进程成为子进程。分叉之前的程序代码是由父进程执行的,但执行后的数据一式两份,俩人都可以分别继续使用。分叉之后的程序代码,两个进程就会分别执行一遍。那么上面这个程序的运行输出会是:
before fork(), pid = 3425
after fork(), pid = 3425
after fork(), pid = 3426

    可以看到 printf("after fork(), pid = %d\n", getpid());被调用了两次(实际上 return 0; 也是被调用了两次)。一次是父进程干的,一次是子进程干的。输出不同是因为两个进程的pid是不同的。需要留意的是,这两次调用的顺序是不可控的,有时候是父前子后,有时候是子前父后。
    使用多进程,为的是执行多个不同的任务。如果每次父进程和子进程都运行的是一样的代码,那就没啥实际的意义了。如何实现分叉后两个进程各自执行各自的代码呢?很简单,根据fork()的返回值来判断。fork()一次调用,却返回两个值:向父进程返回子进程的pid号,向子进程返回0。也有可能只返回一个值-1,创建失败的情况,-1当然是向父进程返回的,因为子进程都没有被创建。那么在两个进程中,就是通过fork()的返回值来区分我到底在哪个进程中的。看这一段代码:
int main(void)
{
    printf("before fork(), pid = %d\n", getpid());
    pid_t p = fork();
    if( p == 0 )
    {
        printf("
in child, pid = %d\n", getpid());
    }
    else
    {

        printf("
in parent, child pid = %d\n", p);
        printf("in parent, pid = %d\n", getpid());
    }
    return 0;
}

    这段程序的运行结果会是:
before fork(), pid = 3425
in child, pid = 3426
in parent, child pid = 3426

in parent, pid = 3425
    后面三句输出的顺序同样是不可控的,也完全有可能是这样子:
before fork(), pid = 3425
in parent, child pid = 3426

in child, pid = 3426
in parent, pid = 3425

    实际上fork()分叉之后,父进程和子进程运行的仍然是同一代码段,只不过可以根据fork()的返回值用if语句来控制,
父进程运行这部分:
        printf("in parent, child pid = %d\n", p);
        printf("in parent, pid = %d\n", getpid());
子进程运行那部分:
        printf("in child, pid = %d\n", getpid());

    二者都会运行到程序结尾的 return 0;然后结束。这个程序还有另一种写法,更容易理解一点,让子进程终结在if(pid=0){...} 里。
int main(void)
{
    printf("before fork(), pid = %d\n", getpid());
    pid_t p = fork();
    if( p == 0 )
    {
        printf("
in child, pid = %d\n", getpid());
        return 0;
    }
    printf("in parent, child pid = %d\n", p);
    printf("in parent, pid = %d\n", getpid());
    return 0;
}

    这段程序和上面那个运行结果是一样的。不可控的运行顺序很烦人,正常情况下会需要等待子进程执行完毕之后再来结束父进程,可以用waitpid()来等待子进程的结束。
int main(void)
{
    printf("before fork(), pid = %d\n", getpid());
    pid_t p = fork();
    if( p == 0 )
    {
        printf("
in child, pid = %d\n", getpid());
        return 0;
    }
    int st;
    waitpid( p, &st, 0);
    printf("in parent, child pid = %d\n", p);
    printf("in parent, pid = %d\n", getpid());
    printf("in parent, child exited with %d\n", st);
    return 0;
}

    这段程序的输出顺序就会确定的:
before fork(), pid = 3425
in child, pid = 3426
in parent, child pid = 3426

in parent, pid = 3425
in parent, child exited with 0

    创建两个子进程应该怎么搞呢,这样子试试看:
int main(void)
{
    printf("before fork(), pid = %d\n", getpid());
    pid_t p1 = fork();
    pid_t p2 = fork();
    if( p1 == 0 )
    {
        printf("
in child 1, pid = %d\n", getpid());
        return 0;
    }
   
if( p2 == 0 )
    {
        printf("
in child 2, pid = %d\n", getpid());
        return 0;
    }

    int st1, st2;
    waitpid( p1, &st1, 0);
    waitpid( p2, &st2, 0);
    printf("in parent, child 1 pid = %d\n", p1);
    printf("in parent, child 2 pid = %d\n", p2);
    printf("in parent, pid = %d\n", getpid());
    printf("in parent, child 1 exited with %d\n", st1);
    printf("in parent, child 2 exited with %d\n", st2);
    return 0;
}


    程序的输出会是:
before fork(), pid = 8624
in child 1, pid = 8626
in child 1, pid = 8625
in child 2, pid = 8627
in parent, child 1 pid = 8625
in parent, child 2 pid = 8627
in parent, pid = 8624
in parent, child 1 exited with 0
in parent, child 2 exited with


    可以看到 in child1... 出现了两次,两次的pid却不相同。说明这段程序是有问题的,想要创建两个子进程,却出现了三个子进程。这句话 printf("in child 1, pid = %d\n", getpid());被执行了两次,这是预料之外的。为什么会这样呢?可以分别来看一下父进程、子进程1号、子进程2号,fork()之后分别应该执行的代码段。
父进程:
    int st1, st2;
    waitpid( p1, &st1, 0);
    waitpid( p2, &st2, 0);
    printf("in parent, child 1 pid = %d\n", p1);
    printf("in parent, child 2 pid = %d\n", p2);
    printf("in parent, pid = %d\n", getpid());
    printf("in parent, child 1 exited with %d\n", st1);
    printf("in parent, child 2 exited with %d\n", st2);
    return 0;
子进程1号:
    pid_t p2 = fork();
    if( p1 == 0 )
    {
        printf("
in child 1, pid = %d\n", getpid());
        return 0;
    }

子进程2号:
    if( p2 == 0 )
    {
        printf("
in child 2, pid = %d\n", getpid());
        return 0;
    }


    可以很明显的看到子进程1号里还有个fork(),也就是说子进程1号又创建了一个子进程,才导致printf("in child 1, pid = %d\n", getpid());被执行了两次。我们可以在子进程1号的代码段中捕捉这个子进程的子进程。
int main(void)
{
    printf("before fork(), pid = %d\n", getpid());
    pid_t p1 = fork();
    pid_t p2 = fork();
    if( p1 == 0 )
    {
        if( p2 == 0 )
        {
            printf("int child 1's child, pid = %d, ppid = %d\n", getpid(), getppid());
            return 0;
        }
        printf("
in child 1, pid = %d\n", getpid());
        return 0;
    }
   
if( p2 == 0 )
    {
        printf("
in child 2, pid = %d\n", getpid());
        return 0;
    }

    int st1, st2;
    waitpid( p1, &st1, 0);
    waitpid( p2, &st2, 0);
    printf("in parent, child 1 pid = %d\n", p1);
    printf("in parent, child 2 pid = %d\n", p2);
    printf("in parent, pid = %d\n", getpid());
    printf("in parent, child 1 exited with %d\n", st1);
    printf("in parent, child 2 exited with %d\n", st2);
    return 0;
}


    程序的输出如下:
before fork(), pid = 8664
int child 1's child, pid = 8666, ppid = 8665
in child 1, pid = 8665
in child 2, pid = 8667
in parent, child 1 pid = 8665
in parent, child 2 pid = 8667
in parent, pid = 8664
in parent, child 1 exited with 0
in parent, child 2 exited with 0


    可以看到“子进程1号的子进程”的ppid就等于子进程1号的pid。为什么要花这么多字来分析这种错误的写法,是因为我曾经被这个错误搞得很糊涂。创建和使用两个甚至更多个子进程的正确方法如下:
int main(void)
{
    printf("before fork(), pid = %d\n", getpid());

    pid_t p1 = fork();
    if( p1 == 0 )
    {
        printf("
in child 1, pid = %d\n", getpid());
        return 0;
    }

    pid_t p2 = fork();
    if( p2 == 0 )
    {
        printf("
in child 2, pid = %d\n", getpid());
        return 0;
    }

    int st1, st2;
    waitpid( p1, &st1, 0);
    waitpid( p2, &st2, 0);
    printf("in parent, child 1 pid = %d\n", p1);
    printf("in parent, child 2 pid = %d\n", p2);
    printf("in parent, pid = %d\n", getpid());
    printf("in parent, child 1 exited with %d\n", st1);
    printf("in parent, child 2 exited with %d\n", st2);
    return 0;
}


    让创建子进程的fork()紧跟着子进程要运行的代码段,子进程之间就不会再互相影响。为了方便的实现多进程,还可以把创建子进程的部分单独列为一个函数,这样更容易理解(像线程一样)。请看我的终极多任务程序框架:
pid_t create_child()
{
    pid_t p = fork();
    if( p == 0 )
    {
        printf("
in child %d\n", getpid());
        //do something
        return 0;
    }
    return p;
}
int main(void)
{
    pid_t p1 = create_child();
    pid_t p2 = create_child();

    int st1, st2;
    waitpid( p1, &st1, 0);
    waitpid( p2, &st2, 0);
    printf("in parent, pid = %d\n", getpid());
    printf("in parent, child 1 exited with %d\n", st1);
    printf("in parent, child 2 exited with %d\n", st2);
    return 0;
}


原创粉丝点击