您的位置:首页 > 其它

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

2011-11-22 10:50 288 查看
刚开始接触进程(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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: