进程之间的管道

问题描述 投票:0回答:2

我尝试编写一个程序,将一串字符转换为大写字母。父进程应该获取字符串并通过管道将其发送给子进程。然后子进程应该从管道中读取,把所有的字母都变成Big letters打印在控制台上。我真的不明白为什么我的管道不工作。当父母甚至没有在其中写东西时,孩子试图阅读管道可能是一个问题。

提前谢谢你。

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

#define BUF 64

/*
Aufgabe: 
Elternprozess: Gibt einen Array mit Klein- und Großbuchstaben mit einer pipe an dem Kindprozess weiter
Kindprozess: Verwanldelt alle Zeichen in Großbuchstaben
*/

int main(void) {
    pid_t pid;

    int fd[2];
    pipe(fd);

    char *buffer = malloc(sizeof(char)*BUF);

    switch(pid = fork()) {
        case -1:
            perror("FEHLER BEI FORK\n");
            break;
        case 0: //Kindprozess
            close(fd[1]);
            char c;
            int i = 0;
            while(1) {
                read(fd[0], &c, 1);
                if(c == '\0') {
                    break;
                } else if(c > 64 || c < 123) {
                    buffer[i] = c;
                    i++;
                }
            }
            for(int i = 0; i < BUF; i++) {
                if(buffer[i] > 96 && buffer[i] < 123) {
                    buffer[i] -= 32;
                }
            }
            printf("Die Zeichen in Großbuchstaben sind:\n\n");
            for(int i = 0; i < BUF; i++) {
                if(buffer[i] != 0 && buffer[i] != 48) {
                    printf("%c", buffer[i]);
                    printf("   ");
                }
            }
            free(buffer);
            break;
        default: //Elternprozess
            close(fd[0]);
            char string1[] = "a b c d \0";
            memset(buffer, 0, BUF);
            strcpy(buffer, string1);
            int len = strlen(buffer) + 1;
            int n;
            int total = 0;
            while(n < len) {
                n = write(fd[1], buffer, strlen(buffer));
                if(n < 0) {
                    perror("FEHLER BEIM SENDEN\n");
                    break;
                }
                total += n;
            }
            printf("Nachricht gesendet.\n");
            
            free(buffer);
            wait(NULL);
            break;
    }
}   

c pipe fork
2个回答
1
投票

问题是读取进程一直读到 NUL 字符,但它永远不会到来,缓冲区溢出,孩子死了。

NUL 字符永远不会被读取,因为它从未被写入。仔细看write loop,稍微简化一点就是:

int len = strlen(buffer) + 1;
int total = 0;
while(n < len) {
   n = write(fd[1], buffer, strlen(buffer));
   total += n;
}

请注意,您在

len
中计算要写入的字节数,包括 NUL,但随后您写入
strlen(buffer)
字节,without 计算 NUL。然后将写入的字节添加到
total
但未使用该变量。因此,您正在无休止地编写没有 NUL 的字符串副本。

解决方案是这样的:

let len = strlen(buffer) + 1;
int total = 0;
while(total < len) {
   n = write(fd[1], buffer + total, len - total);
   if (n < 0) { /*...*/ }
   total += n;
}

0
投票

即使进行了上述更正,您的子进程仍然会调用 Undefined Behavior 写入超出字符串末尾的内容。当你应该只迭代

BUF
次时,你迭代了
i
次。此外,您在孩子身上留下了
i
的阴影:

            int i = 0;
            while(1) {
                read(fd[0], &c, 1);
                if(c == '\0') {
                    break;
                } else if(c > 64 || c < 123) {
                    buffer[i] = c;
                    i++;
                }
            }
            for(int i = 0; i < BUF; i++) {
                ...
            }
            printf("Die Zeichen in Großbuchstaben sind:\n\n");
            for(int i = 0; i < BUF; i++) {
                ...

如果您将

-Wshadow
添加到
gcc
的编译字符串中,您总能发现这个问题。此外,您可以通过简单地确保
buffer
以空字符终止然后迭代
buffer[i]
直到到达空字符来纠正迭代问题。这简化了你的逻辑。

下面迭代

for(i = 0; buffer[i]; i++)
来避免这个问题。这是迭代 nul 终止字符串中字符的正确方法,而无需事先知道长度(这就是 nul 终止字符的用途)

(编辑:-组合循环,用 ctype.h 宏简化)

    case 0: //Kindprozess
      close(fd[1]);
      char c;
      int i = 0;
      while(1) {
        read(fd[0], &c, 1);
        if(c == 0) {
          break;
        }
        else if (isalpha ((unsigned char)c)) {        /* is A-Za-z ? */
          buffer[i] = toupper ((unsigned char)c);   /* make uppwer case */
          i++;
        }
      }
      buffer[i] = 0;    /* nul-terminate at i */
      printf("Die Zeichen in Großbuchstaben sind:\n\n");
      for(i = 0; buffer[i]; i++) {
        printf("%*c", i ? 3 : 0, buffer[i]);    /* write 3 spaces except 1st */
      }
      putchar ('\n');
      free(buffer);
      break;

尽可能避免使用MagicNumbers。不要使用

48
使用
'0'
(这对你如何使用它毫无意义)。不要在表示
>96
时使用
'a'
或在表示
<123
时使用
'z'
。改用文字字符——更具可读性。

此外,包括

ctype.h
并利用为
isalpha()
isupper()
islower()
等提供的宏。比
>96
<123
更具可读性。

事实上,如果您使用

ctype.h
宏,则不需要手动测试字符值。在您的代码中,如果它们是 alpha 字符,您只包含从父级发送的字符。为此,您可以简单地使用
isalpha()

如果使用

toupper()
宏,则在转换为大写字母时无需测试——它只会将小写字母字符转换为大写字母,并在内部提供测试。

在输出行末尾留下尾随空格不是一个好主意。您可以通过在除第一个字符输出之外的所有字符之前写入 3 个空格来控制输出字符之间的 3 个空格。 printf() 中的一个简单的 三元

field-width
使这变得简单,例如

        printf("%*c", i ? 3 : 0, buffer[i]);

试一试其他答案中的更正,您的程序应该按预期执行。

示例使用/输出

$ ./bin/pipe-fork-uppercase
Nachricht gesendet.
Die Zeichen in Großbuchstaben sind:

A  B  C  D

额外的想法

您的代码中还有其他几点有点尴尬。

  • 虽然动态分配 buf 没有任何问题,但对于 64 字节,一个简单的数组声明在堆栈上使用自动存储(如果可以的话)——并且不需要跟踪和
    free()
    完成后的内存。
  • sizeof(char)
    被定义为
    1
    所以它可以在调用
    malloc()
    如果你分配(例如
    malloc(BUF)
    是所有需要的),
  • 父进程中不需要
  • buffer
    。您可以简单地将
    string1
    的内容写入管道。
    buffer
    只能在子进程中声明为自动存储持续时间。很好地定义了常数
    BUF
    ,
  • 当从程序中写入输出时,不要忘记在最终输出后输出一个
    '\n'
    以使程序符合POSIX。如果不这样做,就会在任何未命名为 windows 的操作系统中弄乱下一个终端提示。
    putchar('\n');
    是所有需要的。

把它放在一起,让字符串通过

char string1[] = "a b c d*E_F+G-h ";
更有趣一点,你会:

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

#define BUF 64

/*
Aufgabe: 
Elternprozess: Gibt einen Array mit Klein- und Großbuchstaben mit einer pipe an dem Kindprozess weiter
Kindprozess: Verwanldelt alle Zeichen in Großbuchstaben
*/

int main (void) {
  pid_t pid;

  int fd[2];
  pipe (fd);

  switch (pid = fork()) {
    case -1:
      perror ("FEHLER BEI FORK\n");
      break;
    
    case 0: //Kindprozess
      close (fd[1]);
      
      char c, buffer[BUF];
      int i = 0;
      
      while (1) {
        read (fd[0], &c, 1);
        if (c == 0) {
          break;
        }
        else if (isalpha ((unsigned char)c)) {      /* is A-Za-z ? */
          buffer[i] = toupper ((unsigned char)c);   /* make uppwer case */
          i++;
        }
      }
      
      buffer[i] = 0;    /* nul-terminate at i */
      printf ("Die Zeichen in Großbuchstaben sind:\n\n");
      
      for (i = 0; buffer[i]; i++) {
        printf ("%*c", i ? 3 : 0, buffer[i]);   /* write 3 spaces except 1st */
      }
      putchar ('\n');   /* tidy up with newline */
      break;
    
    default: //Elternprozess
      close (fd[0]);
      
      char string1[] = "a b c d*E_F+G-h ";
      int len = strlen (string1) + 1,
          n,
          total = 0;
      
      while (total < len) {
        n = write (fd[1], string1 + total, len - total);
        if (n < 0) {
          perror ("FEHLER BEIM SENDEN\n");
          break;
        }
        total += n;
      }
      printf ("Nachricht gesendet.\n");
      
      wait (NULL);
      break;
  }
}   

示例使用/输出

$ ./bin/pipe-fork-uppercase
Nachricht gesendet.
Die Zeichen in Großbuchstaben sind:

A  B  C  D  E  F  G  H
© www.soinside.com 2019 - 2024. All rights reserved.