简体   繁体   English

亲子沟通导致系统死机

[英]Communication between parent and child results in system lockup

I am trying to create some communication between two programs by forking in the child program within the parent program.我试图通过在父程序中分叉子程序来在两个程序之间创建一些通信。

When I execute the child program separately, it works.当我单独执行子程序时,它可以工作。 The purpose of it is that if someone types 1, 2, or 3 followed by enter, that program prints that number as a word.这样做的目的是,如果有人在输入 1、2 或 3 后输入,则该程序将该数字打印为一个单词。 But if one presses 0 and enter, the program exits.但是如果按 0 并进入,程序就会退出。

Now I am trying to make the parent program execute the child program in a way where all it does is exit the program while showing the progress of action.现在我试图让父程序以一种方式执行子程序,它所做的只是在显示操作进度的同时退出程序。

When I execute my program, I see:当我执行我的程序时,我看到:

Child to start
Parent running OK

Which suggest the child program is running, otherwise I would see:这表明子程序正在运行,否则我会看到:

Exec failed

So instead of me seeing any actual useful output, the system decides to gradually slow down to the point where at first the mouse cursor doesn't move smoothly when I move the mouse, then It got to the point where it wouldn't respond to the keyboard, so I literally had to hold the power button to reset my computer.因此,我没有看到任何实际有用的 output,而是系统决定逐渐减速到最初鼠标 cursor 在我移动鼠标时不能平稳移动的地步,然后它到了它不会响应的地步键盘,所以我真的不得不按住电源按钮来重置我的电脑。

How do I fix this so that it can work with any program (that I use as a child) that can exit when I press 0 and enter from within it?我该如何解决这个问题,以便它可以与任何可以在我按 0 并从其中进入时退出的程序(我小时候使用的)一起工作?

This is my code for the parent:这是我给父母的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

//Setup child read and write file handle named parr and parw respectively
//and parent read and write file handle named  to parr and parw respectively

#define kidr wrp[0]
#define kidw rdp[1]
#define parw wrp[1]
#define parr rdp[0]


int main(){
  int cmd=0;
  //setup and start pipes
  int wrp[2],rdp[2];
  if (pipe(wrp) == -1 || pipe(rdp) == -1){printf("ERROR: cant run pipes.\n");return -1;}

  //Start fork
  pid_t f=fork();

  if (f > 0){
  int wstat; //wait state data
  char buff[100]; //our data buffer
  close(kidr); //we are parent so close child handles
  close(kidw);
  struct timeval tv;
  fd_set readfds;
  tv.tv_sec = 1;
  tv.tv_usec = 0;
  printf("Parent running OK\n");
  while(1){
      //process other async events here

      pid_t wpid=waitpid(-1,&wstat,WNOHANG);
      if (wpid==-1){printf("Wait PID error\n");break;}
      if (wpid > 0){printf("Children closed OK\n");break;}
      //Process data only when child data is readable via pipe
      FD_ZERO(&readfds);FD_SET(parr, &readfds);
      select(parr+1, &readfds, NULL, NULL, &tv); 
      if(FD_ISSET(parr, &readfds)){
        memset(buff,0,99);
        int rd=read(parr, buff, 50);
        //doesnt seem to reach this point...
        if (rd > 0){
        printf("Got: %s\n", buff);
        }else{
          if (cmd==0){
        printf("sending data...\n");
        char*dat="0\n"; //parent sends 0 and the enter button.
        cmd++; //so this doesn't get called again
        write(parw,dat,strlen(dat));
          }
        }
      }
  }
  //close everything and exit
  close(parr);
  close(parw);
  return 0;
  }
  if (f==0){
  printf("Child to start\n");
  //Child mode.
  //Close parents
  close(parr);close(parw);
  //make stdio as child handles
  dup2(kidr,STDIN_FILENO);dup2(kidw,STDOUT_FILENO);
  //close old child handles
  close(kidw);close(kidr);
  execlp("/path/to/forkt","forkt",NULL);
  //We shouldn't get here unless 'ls' command isnt found
  printf("Exec failed\n");
  _exit(-1);
  }

  if (f==-1){
  //If fork() doesnt work...
  printf("Fork error\n");
  }

  return 0;
}

This is my code for the child and I compiled it so its named forkt.这是我给孩子的代码,我编译了它,所以它命名为 forkt。

#include <stdio.h>
#include <stdlib.h>
int main(){
  printf("The child has started\n\n");
  char c[100];
  while (1){
printf("Enter number or 0 to exit: \n>");
scanf("%s",c);
if (c[0]=='1'){printf("one\n");}
if (c[0]=='2'){printf("two\n");}
if (c[0]=='3'){printf("three\n");}
if (c[0]=='0'){return 0;}
  }
}

Update更新

I took a suggestion of running my parent code through the gdb debugger.我建议通过 gdb 调试器运行我的父代码。

I compiled my code using the gcc -g switch then executed it with gdb a.out我使用 gcc -g 开关编译了我的代码,然后使用 gdb a.out 执行它

Then in gdb, I set a break point to first line of code then used the "run" command then i kept using the "step" command until I found the crashing point which is here:然后在 gdb 中,我在第一行代码中设置了一个断点,然后使用“运行”命令,然后我继续使用“步骤”命令,直到找到这里的崩溃点:

  pid_t f=fork();
  if (f > 0){ // <- right here

This suggests that somehow the child is creating the lockup(?) even though the child runs fine if it is run by itself without a parent?这表明即使孩子在没有父母的情况下自行运行,孩子也能以某种方式创建锁定(?)?

AFAICS, the parent won't write anything to the child until the child sends something back, but the child won't send anything until it gets something from the parent. AFAICS,在孩子发回一些东西之前,父母不会给孩子写任何东西,但孩子在从父母那里得到一些东西之前不会发送任何东西。 That's a deadlock.那是一个僵局。 There's also a problem with buffering.缓冲也有问题。 The pipes are not 'interactive devices' so the output streams are not flushed until the buffer is full, the stream is closed, or you call fflush() .管道不是“交互式设备”,因此 output 流在缓冲区已满、 stream 关闭或您调用fflush()之前不会被刷新。

Here's some alternative but very similar code to yours:这是一些与您的代码非常相似的替代代码:

forkt.c

#include <stdio.h>

int main(void)
{
    printf("The child has started\n\n");
    fflush(stdout);
    char c[100];
    while (1)
    {
        printf("Enter number or 0 to exit:\n>");
        fflush(stdout);
        if (scanf("%s", c) != 1)
            return 0;
        fprintf(stderr, "Child received: [%s]\n", c);
        if (c[0] == '1')
        {
            printf("one\n");
        }
        if (c[0] == '2')
        {
            printf("two\n");
        }
        if (c[0] == '3')
        {
            printf("three\n");
        }
        if (c[0] == '0')
        {
            return 0;
        }
        fflush(stdout);
    }
}

This is mostly noticeable for a collection of calls fflush(stdout) .这对于调用fflush(stdout)的集合最为明显。

parent.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#define kidr wrp[0]
#define kidw rdp[1]
#define parr rdp[0]
#define parw wrp[1]

int main(void)
{
    int cmd = 0;
    int wrp[2], rdp[2];

    fprintf(stderr, "Parent process: PID %d\n", getpid());

    if (pipe(wrp) == -1 || pipe(rdp) == -1)
    {
        fprintf(stderr, "ERROR: cant run pipes.\n");
        exit(1);
    }

    pid_t f = fork();

    if (f == -1)
    {
        fprintf(stderr, "Fork error\n");
        exit(1);
    }

    if (f > 0)
    {
        int wstat;
        char buff[100];
        close(kidr);
        close(kidw);

        fprintf(stderr, "Parent running OK - child %d\n", f);
        while (1)
        {
            pid_t wpid = waitpid(-1, &wstat, WNOHANG);
            if (wpid == -1)
            {
                fprintf(stderr, "Wait PID error\n");
                break;
            }
            if (wpid > 0)
            {
                fprintf(stderr, "Child %d exited\n", wpid);
                break;
            }

            for (cmd = 3; cmd >= 0; cmd--)
            {
                char buffer[30];
                int nb = snprintf(buffer, sizeof(buffer), "%d\n", cmd);
                int wr = write(parw, buffer, strlen(buffer));
                if (wr != nb)
                {
                    fprintf(stderr, "Parent failed to write: %d\n", wr);
                    exit(1);
                }
                fprintf(stderr, "Parent sent: %s", buffer);
                memset(buff, 0, 99);
                int rd = read(parr, buff, 50);
                if (rd > 0)
                {
                    fprintf(stderr, "Got: [[%.*s]]\n", rd, buff);
                }
                else
                {
                    fprintf(stderr, "Parent read failed\n");
                    exit(1);
                }
            }
        }

        close(parr);
        close(parw);
        return 0;
    }

    if (f == 0)
    {
        fprintf(stderr, "Child %d to start\n", getpid());
        close(parr);
        close(parw);
        dup2(kidr, STDIN_FILENO);
        dup2(kidw, STDOUT_FILENO);
        close(kidw);
        close(kidr);
        execlp("forkt", "forkt", NULL);
        fprintf(stderr, "Exec failed\n");
        _exit(-1);
    }

    return 0;
}

The surgery here is more extensive.这里的手术范围更广。

When I ran the code, one time I got the output:当我运行代码时,有一次我得到了 output:

Parent process: PID 94693
Parent running OK - child 94694
Parent sent: 3
Child 94694 to start
Got: [[The child has started

]]
Parent sent: 2
Child received: [3]
Got: [[Enter number or 0 to exit:
>]]
Parent sent: 1
Child received: [2]
Got: [[three
Enter number or 0 to exit:
>]]
Parent sent: 0
Child received: [1]
Got: [[two
Enter number or 0 to exit:
>]]
Child received: [0]
Parent sent: 3
Got: [[one
Enter number or 0 to exit:
>]]
Parent sent: 2
Parent read failed

Note that the prompts from the child are mixed up with the output.请注意,孩子的提示与output混淆了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM