双安培在这个程序中做了什么?

时间:2012-02-15 18:46:37

标签: c process fork ampersand

我正在学习如何使用fork创建进程,我对以下内容感到困惑。这是代码:

int main() {
    int ret = fork();
    // printf("%d\n", ret);
    ret = ret && fork(); /* Here is where I am confused*/
    // print("ret: %d\n", ret);
    if(ret == 0) {
        fork();
    }
    printf("Hello world\n");
    return 1;
}

那么,双安瓿的用途是什么呢? 我使用“printf”运行程序以了解究竟是什么值,但它变得更加混乱,因为第一个“printf”中的输出为0而第二个“printf”为“1”。所以我不太确定双安瓿是做什么的。

我很感激帮助!

8 个答案:

答案 0 :(得分:6)

在C中,双&符号&&是短路logical AND操作。如果你有类似a && b的内容,那么这将评估如下:如果ab都为真,它将返回true,但如果a为false,那么{{ 1}}永远不会被执行。

添加example

b
     

在这个例子中,短路评估保证永远不会调用myfunc(b)。这是因为评估为false。此功能允许两个有用的编程结构。首先,如果第一个子表达式检查是否需要昂贵的计算并且检查评估为假,则可以消除第二个参数中的昂贵计算。其次,它允许一个构造,其中第一个表达式保证条件,否则第二个表达式可能导致运行时错误。

因此,如果int a = 0; if (a && myfunc(b)) { do_something(); } 为真,您的代码只会调用fork()。然后为ret分配0(ret为0)或1(retret均为真)。

答案 1 :(得分:6)

它被称为逻辑AND运算符。这将评估两个操作数的逻辑ANDing结果。该运营商的财产是:

首先评估左侧操作数,如果它是TRUE(非零),则评估右侧操作数。如果它也是真的则整个表达式为真,否则为假。另一方面,如果左侧操作数为FALSE,则右侧操作数未被评估。这样做是因为,由于其中一个操作数是假的,无论是哪个操作数,表达式都会变为false。这称为short circuiting

在您的代码中,如果ret的左侧为真,则仅评估右侧部分,最终调用fork ()系统调用。该调用的返回值与当前值ret进行AND运算,并重新分配给ret

基本上它就像

一样
if (ret == TRUE)
{
  retval = fork ();
  ret = ret && retval;
}

阅读本文:

  

成功时,子进程的PID在父进程中返回,并在子进程中返回0。失败时,在父项中返回-1,不创建子进程,并正确设置errno。

考虑下面的fork树。每个树“节点”显示每个单独语句中执行语句的顺序。一行一行。

    (p1)
+--+ret = fork ();
|   printf 1 shows pid
|   && allows         fork (), ret = 1 = pid1 && pid2 
|   printf 2 shows 1    +
|   `if' not entered    |
|   show hello          |
|                       |    (p3)
|                       +--+ ret = 0 = ret && fork () (this is 0 here)
+-----+                      printf 2 shows 0
      |                      `if' is entered
      |                      fork ()
      |                      show hello
      |                            +
      |                            |
      +                            |
     (p2)                          |
    level 1                        +-------+
    print 0 in 1st printf                  |
    && DOES NOT allow fork ()            (p5)
    print 0 in 2st printf             show hello
   `if' entered
    fork () +-----------+
    show hello          |
                        |
                        +
                      (p4)
                    show hello

这是每个过程的内容。

<强> P1 执行fork ()一次,并在ret中有一个pid(非零)。 打印pid 短路允许执行fork()。由于这是父,它返回另一个pid,它与前一个子pid一起计算,其值为1.因此ret现在包含1,它打印在第二个printf中。如果ret为1,则if不会执行。你好了。

<强> P2 p1的子项,所以ret为0.在第一个printf中打印0。短路不允许fork ()呼叫。输入if正文,并调用fork (),这会产生(p4)。现在(p2)继续打印Hello。

<强> P3 p1的子项,因此fork ()返回0,与ret进行AND运算,并在赋值后使其为0。这是在第一个printf之后生成的,因此只有第二个printf显示为0。 输入if,执行fork (),这使得(p5)。现在p4继续打印Hello。

<强> P4 if身体开始,离开并打印Hello

<强> P5 if身体开始,离开并打印Hello

上面我试图表达进程生成树,并且每个进程中的工作序列在树的进程“node”的每一行中表示。边缘表示spawn,边缘从相应的fork开始。

答案 2 :(得分:2)

这是一个逻辑AND,意味着如果ret为真(非零),并且fork()的结果为真(非零),则赋予ret真,否则分配false(零)到ret

由于此运算符为short-cirucuited,因此仅当fork()为真时才会调用ret

答案 3 :(得分:1)

我认为错误的是对fork()如何工作的误解。如果你在UNIX上,你需要做“man fork”,因为根据我读到的那个:

  

说明         fork()通过复制调用进程来创建一个新进程。该         新的过程,被称为孩子,是一个完全相同的         调用进程,称为父进程,

和..

  

返回值         成功时,子进程的PID将在父进程中返回,并且         孩子返回0。失败时,在父级中返回-1,         没有创建子进程,并且正确设置了errno。

我怀疑可能发生的事情是你可能会看到来自多个分叉进程的输出只会让你感到困惑。你的程序的确切完整输出是什么?

这不太可能是一个短路问题,因为即使第二个失败,至少第一个fork应该成功,因此如果该fork成功,你应该从第一个printfs中的至少一个得到一个pid。 / p>

答案 4 :(得分:0)

这是一个懒惰的逻辑AND函数。如果您需要更多信息,请搜索AND的真值表。这是懒惰的,因为如果ret为false,则fork()将不会被评估,因为任何与false的ANDed始终为false

答案 5 :(得分:0)

这是一个短路逻辑AND。如果ret为0,则它​​将执行fork()。如果没有,它就不会。我可以为你完成代码。

//we fork a child process
int ret = fork();

//ret is 0 if we are in the child process, -1 if fork failed
//otherwise ret is the process id of the child process

//because of this, the fork below executes only within the child process
ret = ret && fork(); /* Here is where I am confused*/

//at this point, if we did fork a process (in the child), then ret is 0 in the child
//then we fork again
if(ret == 0) {
    fork();
}

所以我们有了第一个进程,进程1执行这个代码。让我们假设所有的叉都是成功的(但你应该确保检查一下......我认为它应该在现有的代码中处理,但不是那么明显)。

  • 进程1分叉,创建进程ID为2的子进程。
  • ret在流程1中现在为2,在流程2中为0。
  • 在过程1中,由于ret非零,因此不会发生分叉。
  • 在进程2中,ret为0,因此我们使用进程ID 3分叉子进程。
  • 现在,进入过程1,2和3分别为2,3和0。
  • 现在,进程3将分叉一个新的孩子。

答案 6 :(得分:0)

第一个fork,ret-fork(),将产生3种可能的结果:

  • &GT; 0 =&gt;父母得到孩子的pid
  • = 0 =&gt;儿童过程
  • &LT; 0 =&gt; fork的错误

第二个叉子,ret = ret&amp;&amp; fork()只有在ret非零时才会执行fork()。这可能发生在父良好案例和父错误案例中。该陈述的结果可以是:

  • == 0 =&gt; ret非零,fork()为零。
  • != 0 =&gt; ret非零,fork()非零。

第三个分支,if(ret == 0){fork()},只有在ret为零时才会执行。

那么这一切意味着什么呢?第二个分支看起来很可疑,因为在父成功或失败的情况下,第一个分支的返回值可能是非零的!我不知道这是否是预期的结果,但似乎有些可疑。

如果第一个fork返回在子节点中,则第二个fork会发生,第二个fork不会被执行,但第三个fork会执行。如果第一个fork位于父上下文中,而第二个fork位于子上下文中,则第三个fork也可以执行。

有人检查这个逻辑,因为我永远不会写这种代码。

答案 7 :(得分:0)

在C语言中,&&运算符的行为是这样的:

  1. 计算第一个参数。如果它为零,则不计算第二个参数,结果为0。
  2. 如果第一个参数不为零,则计算第二个参数。如果它为零,答案是0.否则,它是1。
  3. 函数fork向父进程返回0,向父亲返回另一个数字(子代的pid),所以在父进程fork中的第一个ret>0之后,在子进程中,ret==0。如果您取消注释第一个printf,则会得到0和另一个数字。

    然后,你运行你的线。在子项中,ret为0,因此&&的计算在fork前停止,ret保持为0.在父亲中,ret>0,所以它运行fork(),并创建另一个孩子。在父进程中,fork返回正数,因此ret将为1,而在第二个子中,fork返回0,因此ret将为0。 因此,如果您仅取消注释第二个printf,则会获得001(可能按不同的顺序排列)。

    然后,您执行if (ret==0) fork();,因此两个孩子(ret为0)分别创建新流程。现在,您总共有5个进程,因此行Hello world\n将被打印5次。

    (在使用fork时使用输出函数非常危险 - 你有5个进程写入同一个文件而没有任何锁定,所以你可以获得像HHHHHeeeeellllllllllooooo wwwwwooooorrrrrlllllddddd\n\n\n\n\n

    这样的结果