子进程之间的管道

时间:2014-05-05 21:38:09

标签: c string pointers pipe child-process

我编写了一个C程序,它应该创建一定数量的子进程,每个子进程都必须从一个字符串中更改1个字母。从键盘读取字符串和子进程数。

我想用管道来做。它应该像这样工作:父级更改一个字母,然后第一个子级获取父级修改的字符串并再更改一个字母。第二个子进行第一个修改后的字符串(2个字母已经更改)并再更改一个,依此类推。我是C的新手,我不太确定它是如何工作的,特别是管道。

孩子也可以通过管道链接到他们之间,或者他们只能链接到父母,它必须是这样的:第一个孩子改变一个字母,将字符串返回给父母,然后是第二个孩子从那里读取,修改字母并回馈。 如果是这样,有没有办法确保这不会发生:苹果变成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; 
      } 
   }

2 个答案:

答案 0 :(得分:1)

你看到孩子没有输出的一个原因是你把他们的标准输出挂钩到管道的写端,所以当他们写入标准输出时,它会进入管道,而不是屏幕(或者你发送的任何地方)最初程序的标准输出。)

如果孩子不打算执行需要标准输入和标准输出的程序,请不要使用I / O重定向。只需写入和读取管道的正确末端。

如果您有多个孩子,则每个孩子可能需要一个管道,但父进程需要进行创建。您的代码在子代中创建了一个管道;管道是没用的,因为只有孩子知道它。您可能可以使用一个管道完成所有操作,但它会变为不确定的子项将运行的顺序。如果确定性很重要,请使用多个pipe()调用,并且至少两倍close()次调用。< / p>

单管道解决方案

#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

答案 1 :(得分: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;
}