首页 > 解决方案 > 如何检查父子进程的状态?

问题描述

我刚刚编写了一段简单的代码来检查子进程和父进程的运行方式。但我没有得到想要的输出。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
    pid_t x;
    int n=1;
    x=fork();
    if(x>0)
    {
        n+=2;
        printf("Parent process exist %d\n",n);
    }
    else if(x==0)
    {
        n+=5;
        printf(" Child process %d\n ",n);
    }
    printf("done %d",n);
    return 0;
}

代码很简单,但是否有任何隐藏的问题会产生意外的输出?

标签: unixprocessoperating-systemforksystem-calls

解决方案


警告:答案不是律师规则!


是的。n+=5不是原子操作。它由三个“子操作”组成:加载n、添加5、存储n

除非它甚至没有这样做,因为编译器可以自由地“嘿;n当我可以将值保存在寄存器中时,所有这些加载和存储到 RAM 中都是毫无意义的”。声明变量volatile int来解决这个问题。

通过此示例编译和执行代码(带有易失性更改)可以看出非原子事物的重要性:

  0 SYSCALL fork_into_register_zero
  1 STORE 1 INTO RAM #386
  2 COMPARE REGISTER #0 TO 0
  3 JUMP IF <= TO INSTRUCTION #23
  4 LOAD RAM #386 INTO REGISTER #1
  5 ADD 2 TO REGISTER #1
  6 STORE REGISTER #1 INTO RAM #386
  7 LOAD "Parent process exist " INTO REGISTER #0
  8 LOAD 1 INTO REGISTER #1
  9 SYSCALL output_string_from_register_zero_to_file_descriptor_from_register_one
 10 LOAD 1000000 INTO REGISTER #2
 11 LOAD RAM #386 INTO REGISTER #1
 12 STORE REGISTER #1 INTO REGISTER #0
 13 DIVBY REGISTER #2 TO REGISTER #0
 14 ADD '0' TO REGISTER #0
 15 SYSCALL putch_from_register_zero
 16 MODBY REGISTER #2 TO REGISTER #1
 17 DIVBY 10 TO REGISTER #2
 18 COMPARE REGISTER #2 TO 0
 19 JUMP IF > TO INSTRUCTION #12
 20 STORE '\n' TO REGISTER #0
 21 SYSCALL putch_from_register_zero
 22 COMPARE 1 TO 0
 23 JUMP IF != TO INSTRUCTION 44
 24 LOAD RAM #386 INTO REGISTER #1
 25 ADD 5 TO REGISTER #1
 26 STORE REGISTER #1 INTO RAM #386
 27 LOAD " Child process " INTO REGISTER #0
 28 LOAD 1 INTO REGISTER #1
 29 SYSCALL output_string_from_register_zero_to_file_descriptor_from_register_one
 30 LOAD 1000000 INTO REGISTER #2
 31 LOAD RAM #386 INTO REGISTER #1
 32 STORE REGISTER #1 INTO REGISTER #0
 33 DIVBY REGISTER #2 TO REGISTER #0
 34 ADD '0' TO REGISTER #0
 35 SYSCALL putch_from_register_zero
 36 MODBY REGISTER #2 TO REGISTER #1
 37 DIVBY 10 TO REGISTER #2
 38 COMPARE REGISTER #2 TO 0
 39 JUMP IF > TO INSTRUCTION #12
 40 STORE '\n' TO REGISTER #0
 41 SYSCALL putch_from_register_zero
 42 STORE ' ' TO REGISTER #0
 43 SYSCALL putch_from_register_zero
 44 LOAD "Done " INTO REGISTER #0
 45 LOAD 1 INTO REGISTER #1
 46 SYSCALL output_string_from_register_zero_to_file_descriptor_from_register_one
 47 LOAD 1000000 INTO REGISTER #2
 48 LOAD RAM #386 INTO REGISTER #1
 49 STORE REGISTER #1 INTO REGISTER #0
 50 DIVBY REGISTER #2 TO REGISTER #0
 51 ADD '0' TO REGISTER #0
 52 SYSCALL putch_from_register_zero
 53 MODBY REGISTER #2 TO REGISTER #1
 54 DIVBY 10 TO REGISTER #2
 55 COMPARE REGISTER #2 TO 0
 56 JUMP IF > TO INSTRUCTION #12
 57 STORE '\n' TO REGISTER #0
 58 SYSCALL putch_from_register_zero
 59 STORE 0 TO REGISTER #0
 60 RETURN

这里有一个内核级别的putch操作stdout,因为我认为一致性对于重新输入 23 行假代码并不重要。请注意,SYSCALLs 由内核处理,因此是原子的(至少在处理文件描述符的输出时)。

这些指令中的每一个都是原子的。但是, 之后的所有内容都会SYSCALL fork_into_register_zero以不同的 , 值运行两次REGISTER #0,并且可以以任何方式交错。让它沉入其中。最后可能n不会是8。事实上,输出可能是这样的:

 Child process 3
 Done Parent process exist 33

Done 3

好像不太对?那是给你穿线!


推荐阅读