c - 子进程之间的管道

标签 c string pointers pipe child-process

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

我想用管道来做。它应该像这样工作: parent 改变一个字母,然后第一个 child 接受 parent 修改的字符串并再改变一个字母。第二个 child 接受第一个修改的字符串(2 个字母已经更改)并再更改一个,依此类推。我是 C 的新手,不太确定它是如何工作的,尤其是管道。

children 也可以通过管道在他们之间链接,或者他们只能链接到父级并且它必须是这样的:第一个 child 改变一个字母,将字符串返回给父级然后第二个 child 从那里读取,修改字母并返回。 如果是这样,有什么方法可以确保不会发生这种情况:Apples 变成 AppleD,然后是 AppleX,然后是 AppleQ?

例如:

input:

3 Apples  

output:

Applex Appldx Apqldx

我的问题是:我没有从 child 那里得到任何输出。不确定我做错了什么。非常感谢您的帮助,提前致谢!

这是我的代码:

#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; 
      } 
   }

最佳答案

你看不到 child 的输出的一个原因是你将他们的标准输出挂接到管道的写入端,所以当他们写入标准输出时,它进入管道,而不是屏幕(或你发送的任何地方程序的标准输出为原始)。

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

如果你有多个 child ,你可能需要每个 child 一个管道,但父进程将需要进行创建。您的代码在 child 中创建了一个管道;那个 pipe 没用,因为只有 child 知道。您可能可以用一个管道完成所有操作,但它变得不确定子项将按哪个顺序运行。如果确定性很重要,请使用多个 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() 的误用。它确保 children 改变消息中的不同字母。否则,他们最终都会更改消息中相同“随机”位置的相同“随机”字母。

如果您愿意,您可以创建一个多管道解决方案。我从单管道解决方案中得到了看似确定性的行为,但不能保证顺序。例如,如果每个 child 使用 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

关于c - 子进程之间的管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23482355/

相关文章:

java - 是否可以停止 JNI 调用

c - 打印 float 然后打印数组时出现奇怪的行为

C++ 在函数中传递指针地址

c++ - 多态性 : "A pointer to a bound function may only be used to call the function"

c++ - 为什么指向 null 的指针计算结果为 true?

c - 静态库中外部符号的预期签名

c - 在C中的双向链表中插入

javascript - 替换符合条件的括号

c# - 如何用rdlc中的新行替换定界符

java - 在字符串中每隔 ""之前或 160 个字符处插入 <SPLIT>