子进程之间的管道通信

3
我写了一个C程序,旨在创建一定数量的子进程,每个子进程都需要更改字符串中的一个字母。从键盘读取字符串和子进程数量。
我希望使用管道实现这个功能。它应该像这样工作:父进程更改一个字母,然后第一个子进程获取由父进程修改的字符串并更改一个字母。第二个子进程获取由第一个子进程修改的字符串(已经更改了2个字母)并更改一个字母,以此类推。我是C语言新手,对所有这些不太确定,特别是管道。
另外,这些子进程能否通过管道相互链接,或者它们只能与父进程链接,并且必须像这样进行:第一个子进程更改一个字母,将字符串返回给父进程,然后第二个子进程从那里读取,修改字母并返回。如果是这样的话,有没有办法确保不会发生这种情况:Apples变成AppleD,然后变成AppleX,然后变成AppleQ?
例如:
input:

3 Apples  

output:

Applex Appldx Apqldx

我的问题是:我没有从子元素中获得任何输出。不确定我做错了什么。希望能得到帮助,非常感谢!
以下是我的代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>

void error(char* msg)
   {
   fprintf(stderr, "%s\n", msg);
   exit(1);
   }

char* modify(char msg[])
   {
   srand(time(NULL));
   int pos1=rand()%((int)strlen(msg));
   srand(time(NULL));
   int pos2=rand()%26;
   srand(time(NULL));
   int big=rand()%2;
   if(big==1) 
      {
      msg[pos1]=(char)(((int)'A')+pos2);
      }
   else 
      {
      msg[pos1]=(char)(((int)'a')+pos2);
      }

   return msg;
   }

int main(int argc, char *argv[])
   {
   if(argc!=3) 
      {
      error("Wrong number of arguments\n");
      }

   int nrch;
   nrch=atoi(argv[1]);
   char* msg=argv[2];

   printf("Parent: erhalten: %s\n", msg);
   int i=0;
   msg=modify(argv[2]);
   printf("Parent: weiter: %s\n", msg);
   pid_t pids[10];
   int fd[2];

   if(pipe(fd) == -1) 
       {
       error("Can't create the pipe");
       }

   dup2(fd[1], 1);
   close(fd[0]);
   fprintf(stdout, msg);

   /* Start children. */
   for (i = 0; i < nrch; ++i) 
       {
       if ((pids[i] = fork()) < 0) 
          {
          error("Can't fork process");
          } 
       else if (pids[i] == 0) 
          {
          dup2(fd[0], 0);
          close(fd[1]);
          fgets(msg,255,stdin);
          printf("child%d: erhalten: %s\n", (i+1), msg);
          modify(msg);
          printf("child%d: weiter: %s\n", (i+1), msg);
          if (pipe(fd) == -1) 
             {
             error("Can’t create the pipe");
             }

          fprintf(stdout, msg);
          dup2(fd[1], 1);
          close(fd[0]);
          exit(0);
          }
       }

   /* Wait for children to exit. */
   int status;
   pid_t pid;
   while (nrch > 0) 
      {
      pid = wait(&status);
      printf("Child with PID %ld exited with status 0x%x.\n", (long)pid, status);
      --nrch; 
      } 
   }

我在你的代码中没有看到任何“管道”。对我来说,它看起来只是一个普通的函数。 - James Anderson
请注意,多次调用srand()会使其失去作用。实际上,这几乎保证了每次调用rand()都会返回相同的值,因为您不断将随机种子重置为相同的值(因为现代计算机速度很快,而time()只在每秒钟更改其报告的值)。 - Jonathan Leffler
2个回答

1

你看不到子进程的输出是因为你将它们的标准输出钩到了管道的写端口,所以当它们输出到标准输出时,会进入管道而不是屏幕(或者其它你最初指定输出程序的地方)。

如果子进程不需要执行一个需要标准输入和标准输出的程序,则不要使用I/O重定向。只需写入和读取正确的管道端口。

如果你有多个子进程,你可能需要每个子进程有一个管道,但父进程需要进行创建。你的代码在子进程中创建了一个管道,但那个管道是没有用处的,因为只有该子进程知道它的存在。你可能可以使用一个管道来完成所有任务,但这样会变得无法确定子进程的执行顺序。如果确定性很重要,请使用多个pipe()调用,并且至少调用两次close()

单管道解决方案

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

static void error(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    putc('\n', stderr);
    exit(1);
}

static void modify(char msg[])
{
    int pos1 = rand() % ((int)strlen(msg));
    int pos2 = rand() % 26;
    int big = rand() % 2;
    if (big == 1)
        msg[pos1] = (char)(((int)'A') + pos2);
    else
        msg[pos1] = (char)(((int)'a') + pos2);
}

static int read_pipe(int fd, char *buffer, size_t buflen)
{
    int nbytes = read(fd, buffer, buflen);
    if (nbytes <= 0)
        error("Unexpected EOF or error reading pipe");
    assert((size_t)nbytes < buflen);
    buffer[nbytes] = '\0';
    return nbytes;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
        error("Usage: %s number 'message'", argv[0]);
    srand(time(NULL));

    int nrch = atoi(argv[1]);
    char *msg = argv[2];
    size_t len = strlen(msg);

    printf("Parent: erhalten: %s\n", msg);
    modify(msg);
    printf("Parent: weiter: %s\n", msg);

    int fd[2];

    if (pipe(fd) == -1)
        error("Can't create the pipe");

    if (write(fd[1], msg, len) != (ssize_t)len)
        error("Failed to write to pipe");

    /* Start children. */
    for (int i = 0; i < nrch; ++i)
    {
        int pid;
        if ((pid = fork()) < 0)
            error("Can't fork process");
        else if (pid == 0)
        {
            char buffer[255];
            int nbytes = read_pipe(fd[0], buffer, sizeof(buffer));
            printf("child%d: erhalten (%d): %s\n", (i + 1), nbytes, buffer);
            modify(buffer);
            printf("child%d: weiter (%d): %s\n", (i + 1), nbytes, buffer);
            write(fd[1], buffer, nbytes);
            exit(0);
        }
        else
            printf("Random: %d\n", rand());
    }

    /* Wait for children to exit. */
    while (nrch > 0)
    {
        int status;
        pid_t pid = wait(&status);
        printf("Child with PID %ld exited with status 0x%.4X.\n", (long)pid, status);
        --nrch;
    }

    char buffer[255];
    int nbytes = read_pipe(fd[0], buffer, sizeof(buffer));

    printf("Parent: weiter (%d): %s\n", nbytes, buffer);
    return 0;
}

示例输出

文件中的代码 p1.c

$ make p1 && ./p1 4 "Absolutely nothing to do with me"
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror p1.c -o p1
Parent: erhalten: Absolutely nothing to do with me
Parent: weiter: AbsolutEly nothing to do with me
Random: 1120753102
child1: erhalten (32): AbsolutEly nothing to do with me
Random: 918317477
child1: weiter (32): AbsolutEly notzing to do with me
child2: erhalten (32): AbsolutEly notzing to do with me
child2: weiter (32): AbsolwtEly notzing to do with me
Random: 196864950
child3: erhalten (32): AbsolwtEly notzing to do with me
child3: weiter (32): AbsolwtEly notzing to ao with me
Random: 1584398270
Child with PID 42928 exited with status 0x0000.
Child with PID 42927 exited with status 0x0000.
Child with PID 42926 exited with status 0x0000.
child4: erhalten (32): AbsolwtEly notzing to ao with me
child4: weiter (32): AbsolwtEly notzing to ao with Ue
Child with PID 42929 exited with status 0x0000.
Parent: weiter (32): AbsolwtEly notzing to ao with Ue
$

请注意循环中偶尔出现的rand()的用法。它确保孩子们改变消息中的不同字母。如果没有这个,他们最终都会在消息中相同的“随机”位置更改相同的“随机”字母。
如果您愿意,可以创建一个多管道解决方案。虽然单管道解决方案的顺序不能保证,但我似乎得到了确定性行为。例如,每个孩子使用nanosleep()或等效的函数等待一段随机延迟:
            struct timespec nap = { .tv_sec = 0, .tv_nsec = (rand() % 1000) * 1000000 };
            nanosleep(&nap, 0);

那么你就会在子进程中得到一个任意的序列。例如:

Parent: erhalten: Absolutely nothing to do with me
Parent: weiter: Absolutely nothinglto do with me
Random: 2028074573
Random: 988903227
Random: 1120592056
Random: 359101002
child4: erhalten (32): Absolutely nothinglto do with me
child4: weiter (32): vbsolutely nothinglto do with me
Child with PID 43008 exited with status 0x0000.
child3: erhalten (32): vbsolutely nothinglto do with me
child3: weiter (32): vbsolutelyGnothinglto do with me
Child with PID 43007 exited with status 0x0000.
child2: erhalten (32): vbsolutelyGnothinglto do with me
child2: weiter (32): vbsolutelyGnothinglto doawith me
Child with PID 43006 exited with status 0x0000.
child1: erhalten (32): vbsolutelyGnothinglto doawith me
child1: weiter (32): vbsolutelyGnothinglto doawimh me
Child with PID 43005 exited with status 0x0000.
Parent: weiter (32): vbsolutelyGnothinglto doawimh me

请勿更改“erhalten”或“weiter”。这些单词在问题中,尊重提问者意味着它们应该保留在答案中。它们彼此不同;它们明确标记了之前和之后的状态。它们的存在不应该比使用变量名如“nrch”、“pids”或“argv”更难理解。现实点吧,只会说英语的人! - Jonathan Leffler

0
尝试根据以下内容更改您的代码,不确定这是否完全符合您的要求。无论如何,孩子们正在奔跑...
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        error("Wrong number of arguments\n");
    }
    int nrch;
    nrch = atoi(argv[1]);
    char *msg = argv[2];

    printf("Parent: erhalten: %s\n", msg);
    int i = 0;
    msg = modify(argv[2]);
    printf("Parent: weiter: %s\n", msg);
    pid_t pids[10];
    int fd[2];
    /* Start children. */
    for (i = 0; i < nrch; ++i)
    {
        if ((pids[i] = fork()) < 0)
        {
            error("Can't fork process");
        }
        if (pipe(fd) == -1)
        {
            error("Can't create the pipe");
        }
        //    printf ( "  pids[i] %d , i %d \n", pids[i] , i);
        if (pids[i] == 0)
        {
            if (dup2(fd[0], 0) == -1)
            {
                error("Can't dup2 (A)");
            }
            close(fd[1]);
            fgets(msg, 255, stdin);
            printf("child%d: erhalten: %s\n", (i + 1), msg);
            modify(msg);
            printf("child%d: weiter: %s\n", (i + 1), msg);

            fprintf(stdout, msg);
        }
        else
        {
            // printf("in else i= %d \n",i);
            if (dup2(fd[1], 0) == -1)
            {
                error("Can't dup2 (B)");
            }
            close(fd[0])
            exit(0);
        }
    }

    /* Wait for children to exit. */
    int status;
    pid_t pid;
    while (nrch > 0)
    {
        pid = wait(&status);
        if (pid > -1)
            printf("Child with PID %ld exited with status 0x%x.\n", (long)pid, status);
        --nrch;
    }
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接