【问题标题】:pipe and fork result in wrong and messy output管道和叉子导致错误和混乱的输出
【发布时间】:2017-08-02 08:20:16
【问题描述】:

我在处理管道和子进程时遇到了一些问题。

我需要

  1. 使用 fork() 创建四个孩子
  2. 使用 pipe() 在父级和每个子级之间建立双向通信
  3. 从包含多个字符串的文件 card.txt 中读取数据
  4. 以循环方式将字符串分配给每个孩子 A B C D E F G H,孩子 1 得到 A E,孩子 2 得到 B F,依此类推
  5. 选择一个成员变量 -> 使用 write() 和 read() 以及 close() 将变量发送到父级以关闭未使用的管道端
  6. 打印父进程中收到的变量

预期的输出是:

$ ./a.out 1C < card.txt
    Child : 1, pid 1593 : 
    <S2 S3 S9 ><H9 H6 HA H8 ><C6 CK ><D8 DQ D7 D3 >
    Child : 2, pid 1594 : 
    <SA S6 S7 S4 ><H4 HJ H7 ><CQ C9 CT ><DT D2 D9 >
    Child : 3, pid 1595 : 
    <SQ S5 ><H2 ><C7 C5 C8 CA C4 CJ C3 ><DA D5 > 
    Child : 4, pid 1596 : 
    <S8 SJ SK ST ><HK HT H5 H3 HQ ><C2 ><D4 D6 >
    child 1: H9 
    parent: child 1 played H9 
    child 2: CQ 
    parent: child 2 played CQ  
    child 3: H2
    parent: child 3 played H2 
    child 4: HK 
    parent: child 4 played HK 

但是,实际输出如下:

parent: child 1 played (null)parent: child 2 played (null)parent: child 3 played (null)parent: child 4 played (null)

这是为什么呢?如何解决问题?

代码创建了这个输出:

#include <stdio.h>
#include <string.h>

#define BUFFERSIZE 51 
 int i=0;
   int currpid, s; 
  char *buf[BUFFERSIZE]; 
    char *array[BUFFERSIZE];
    int n;
    char *buffer[100]; 
/** child to parent pipe */
    int child_parent[2];
    /** parent to child pipe */
    int parent_child[2];

     /** child2 to parent pipe */
    int child2_parent[2];
    /** parent to child2 pipe */
    int parent_child2[2];

     /** child3 to parent pipe */
    int child3_parent[2];
    /** parent to child3 pipe */
    int parent_child3[2];

     /** child4 to parent pipe */
    int child4_parent[2];
    /** parent to child4 pipe */
    int parent_child4[2];

void childFunction(){
    int j;
         for( i = s+1; i < BUFFERSIZE; i += 4 )
         {   
            buf[j] = array[i];  
            j++; 
         } 
         printf("\n<");
         int r;
         char *e;
         for( r = 0; r < j; r++ )
         { 
            int index;
            e = strchr(buf[r],'S');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){

                    printf("%s ", buf[r]) ; 
                } 
            }  
        } 
        printf(">");
        printf("<");
        for( r = 0; r < j; r++ )
        { 
            int index;
            e = strchr(buf[r],'H');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){ 
                    printf("%s ", buf[r]) ; 
                } 
            }  
        }   
        printf(">");
        printf("<");
        for( r = 0; r < j; r++ )
        { 
            int index;
            e = strchr(buf[r],'C');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){
                    printf("%s ", buf[r]) ; 
                } 
            }  
        } 
        printf(">");
        printf("<");
        for( r = 0; r < j; r++ )
        { 
            int index;
            e = strchr(buf[r],'D');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){
                    printf("%s ", buf[r]) ; 
                }
            }  
        } 
        printf(">\n");
        switch (s){
            case 0:
              close(parent_child[1]);
              close(parent_child[0]); 
              close(child_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child_parent[1], &buffer[0], strlen(buffer[0]));
              close(child_parent[1]); 
              break;
            case 1:
              close(parent_child2[1]);
              close(parent_child2[0]); 
              close(child2_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child2_parent[1], &buffer[0], strlen(buffer[0]));
              close(child2_parent[1]); 
              break;
            case 2:
              close(parent_child3[1]);
              close(parent_child3[0]); 
              close(child3_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child3_parent[1], &buffer[0], strlen(buffer[0]));
              close(child3_parent[1]); 
              break;
            case 3:
              close(parent_child4[1]);
              close(parent_child4[0]); 
              close(child4_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child4_parent[1], &buffer[0], strlen(buffer[0]));
              close(child4_parent[1]); 
              break;
        }
} 
void parentFunction(){

                switch (s){
                    case 0:
                      close(child_parent[1]);
                      close(parent_child[1]);  
                      close(parent_child[0]);
                      read(child_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child_parent[0]);
                      break;
                     case 1:
                      close(child2_parent[1]);
                      close(parent_child2[1]);  
                      close(parent_child2[0]);
                      read(child2_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child2_parent[0]);
                      break;
                     case 2:
                      close(child3_parent[1]);
                      close(parent_child3[1]);  
                      close(parent_child3[0]);
                      read(child3_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child3_parent[0]);
                      break;
                     case 3:
                      close(child4_parent[1]);
                      close(parent_child4[1]);  
                      close(parent_child4[0]);
                      read(child4_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child4_parent[0]);
                      break;   
                }
}
int main(int argc, char *argv[])
{
    int ch; 
    ssize_t rread;
    char *line = NULL;
    size_t len = 0; 
    while (rread = getdelim( &line, &len, '\0', stdin) != -1) { 
    }  

    array[i] = strtok(line," ");

    while(array[i]!=NULL)
    {
        array[++i] = strtok(NULL," ");
    } 

    int childlimit = 4;
    int childpids[childlimit];
    int currpid; 


    if (pipe(child_parent) == 0 && pipe(parent_child) == 0 && pipe(child2_parent) == 0 && pipe(parent_child2) == 0 && pipe(child3_parent) == 0 && pipe(parent_child3) == 0 && pipe(child4_parent) == 0 && pipe(parent_child4) == 0)
    {
      for(s=0; s<childlimit; s++){
        switch(currpid = fork()){
        case 0: 
         printf("Child : %d, pid %d : ", s+1, getpid() );
         childFunction(); 
         break;
        case -1:
         printf("Error when forking\n");
         return 1;
        default:
        // in the father
        childpids[s] = currpid;  
        parentFunction();
        break;
        } 
          }   
    //wait for all child created to die
    waitpid(-1, NULL, 0); 
    }
}

【问题讨论】:

  • 你听说过这些叫做“函数”的东西吗?它们有助于使代码更清晰。主函数中子处理的代码太多了。将流程管理保留在main();将字符串处理和 I/O 操作委托给仅在子进程中运行的函数。您并没有真正在孩子中关闭足够的文件描述符。如果父项需要写入其子项,则您在父项中关闭了太多文件描述符;如果父母不需要写信给孩子,你为什么要为从父母到孩子的管道烦恼?
  • @JonathanLeffler,更新后的代码将父子代码移动到不同的函数中,在我的实现中,它需要父子写入,但此时由于问题尚未开发。
  • 在函数中使用全局变量i 进行循环控制是一场灾难。有时,全局变量是必要的——当它们需要时,你应该使用它们。大多数情况下,它们不是——当它们不是时,你不应该使用它们。单字母全局变量名称很少适用。 (最好在每个需要它的函数中重复i 的定义,而不是像你所做的那样。)这使得你的代码很难分析。您的字符串读取循环仅保留最后一个“行”(以空值结尾的“行”)。我想这是在一次操作中吞食整个文件的一种方式。
  • 您的代码不会将字符串的选择写入子项——您根本没有使用父子管道。这违背了您为程序提供的规范。因为你有 8 个管道,16 个描述符,你的子进程应该分别关闭 14 个描述符——它们只需要保持从父进程到子进程的管道读取端和从子进程到子进程的写入端保持打开状态。父母。您拨打waitpid() 等待任何一个孩子死去;你需要一个循环来等待他们全部死去。
  • 如果您提供了输入数据,您应该从中获得预期的输出数据,这也会有很大帮助。它是如何布置的并不明显。

标签: c arrays pipe fork


【解决方案1】:

当您开始使用 child2_parentparent_child2 之类的名称时,会有一个数组试图从您的变量中转义。如果你有 4 组这些变量,那就太可怕了。

代码有很多很多问题。这是一个彻底的重写。错误报告给stderr。大多数系统调用都会被检查。有已读取数据的诊断打印输出。有一个函数split_string() 将字符串拆分为空格上的单词。它用于父组件和子组件。孩子们不再能够访问字符串数据——所有的全局变量都消失了。

代码仍远未达到最佳状态。在启动孩子之前没有特别需要打开所有管道;该过程是同步的。依次为每个孩子创建管道是可行的,甚至是明智的。就目前而言,每个孩子只关闭了四分之一的可用管道描述符。

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

enum { MAX_KIDS  = 4 };
enum { LIST_SIZE = 1024 };
enum { MAX_WORDS = LIST_SIZE / 3 };

static int split_string(char *data, char *words[], int max_words)
{
    int num_words = 0;
    char *src = data;
    char *token;
    while ((token = strtok(src, " \n\t")) != NULL)
    {
        words[num_words++] = token;
        if (num_words >= max_words)
            break;
        src = NULL;
    }
    return num_words;
}

static void childFunction(int s, int f_pipe[2], int t_pipe[2])
{
    srand(getpid());
    close(f_pipe[1]);
    close(t_pipe[0]);
    char data[LIST_SIZE];
    int nbytes;
    if ((nbytes = read(f_pipe[0], data, sizeof(data))) <= 0)
    {
        fprintf(stderr, "child %d (PID %d): failed to read pipe\n", s, (int)getpid());
        exit(1);
    }
    data[nbytes] = '\0';
    close(f_pipe[0]);

    char *words[MAX_WORDS];
    int num_words = split_string(data, words, MAX_WORDS);
    char *word = words[rand() % num_words];
    int len = strlen(word);
    if (write(t_pipe[1], word, len) != len)
    {
        fprintf(stderr, "child %d (PID %d): failed to write [%s] to pipe\n", s, (int)getpid(), word);
        exit(1);
    }
    close(t_pipe[1]);
    exit(0);
}

static void parentFunction(int s, int c_pid, int t_pipe[2], int f_pipe[2], char *words[], int max_words)
{
    close(t_pipe[0]);
    close(f_pipe[1]);

    char buffer[LIST_SIZE];
    char *data = buffer;
    const char *pad = "";
    for (int i = s; i < max_words; i += MAX_KIDS)
    {
        int n = snprintf(data, (size_t)(buffer + sizeof(buffer) - data), "%s%s", pad, words[i]);
        data += n;
        pad = " ";
    }
    printf("Send [%s] to child %d\n", buffer, s);

    /* Write list of words to child */
    int nbytes = data - buffer;
    if (write(t_pipe[1], buffer, nbytes) != nbytes)
    {
        fprintf(stderr, "parent: failed to write to child %d (PID %d)\n", s, c_pid);
        exit(1);
    }
    close(t_pipe[1]);

    /* Read selected word from child */
    nbytes = read(f_pipe[0], buffer, sizeof(buffer));
    if (nbytes <= 0)
    {
        fprintf(stderr, "parent: failed to read from child %d (PID %d)\n", s, c_pid);
        exit(1);
    }
    buffer[nbytes] = '\0';
    close(f_pipe[0]);

    printf("parent: child %d (PID %d) played %s\n", s, c_pid, buffer);
}

int main(void)
{
    ssize_t rread;
    char *line = NULL;
    size_t len = 0;
    if ((rread = getdelim(&line, &len, '\0', stdin)) == -1)
    {
        fprintf(stderr, "Unexpected EOF on standard input\n");
        return 1;
    }

    printf("line [%s]\n", line);

    char *wordlist[MAX_WORDS];
    int num_words = split_string(line, wordlist, MAX_WORDS);

    printf("Word list:");
    int i;
    for (i = 0; i < num_words; i++)
    {
        if (i % 10 == 0 && i > 0)
            printf("%10s", "");
        printf(" %2d [%s]", i, wordlist[i]);
        if (i % 10 == 9)
            putchar('\n');
    }
    if (i % 10 != 0)
        putchar('\n');
    printf("%d words\n", i);

    for (int kid = 0; kid < MAX_KIDS; kid++)
    {
        printf("Words for child %d:", kid);
        for (int word = kid; word < i; word += MAX_KIDS)
            printf(" [%s]", wordlist[word]);
        putchar('\n');
    }

    int child_parent[MAX_KIDS][2];
    int parent_child[MAX_KIDS][2];

    for (int i = 0; i < MAX_KIDS; i++)
    {
        if (pipe(child_parent[i]) != 0 || pipe(parent_child[i]) != 0)
        {
            fprintf(stderr, "failed to create pipes for child %d\n", i);
            exit(1);
        }
    }

    for (int s = 0; s < MAX_KIDS; s++)
    {
        int currpid = fork();
        if (currpid < 0)
        {
            fprintf(stderr, "Error when forking\n");
            exit(1);
        }
        else if (currpid == 0)
        {
            printf("Child : %d, pid %d:\n", s, getpid() );
            childFunction(s, parent_child[s], child_parent[s]);
        }
        else
            parentFunction(s, currpid, parent_child[s], child_parent[s], wordlist, num_words);
    }

    int corpse;
    int status;
    while ((corpse = waitpid(-1, &status, 0)) > 0)
        printf("PID %d died with exit status 0x%.4X\n", corpse, status);
}

给定一个数据文件:

S2 SA SQ S8 S3 S6 S5 SJ S9 S7 H2 SK H9 S4 C7
ST H6 H4 C5 HK HA HJ C8 HT H8 H7 CA H5 C6 CQ
C4 H3 CK C9 CJ HQ D8 CT C3 C2 DQ DT DA D4 D7
D2 D5 D6 D3 D9

程序的一个示例输出是:

line [S2 SA SQ S8 S3 S6 S5 SJ S9 S7 H2 SK H9 S4 C7
ST H6 H4 C5 HK HA HJ C8 HT H8 H7 CA H5 C6 CQ
C4 H3 CK C9 CJ HQ D8 CT C3 C2 DQ DT DA D4 D7
D2 D5 D6 D3 D9
]
Word list:  0 [S2]  1 [SA]  2 [SQ]  3 [S8]  4 [S3]  5 [S6]  6 [S5]  7 [SJ]  8 [S9]  9 [S7]
           10 [H2] 11 [SK] 12 [H9] 13 [S4] 14 [C7] 15 [ST] 16 [H6] 17 [H4] 18 [C5] 19 [HK]
           20 [HA] 21 [HJ] 22 [C8] 23 [HT] 24 [H8] 25 [H7] 26 [CA] 27 [H5] 28 [C6] 29 [CQ]
           30 [C4] 31 [H3] 32 [CK] 33 [C9] 34 [CJ] 35 [HQ] 36 [D8] 37 [CT] 38 [C3] 39 [C2]
           40 [DQ] 41 [DT] 42 [DA] 43 [D4] 44 [D7] 45 [D2] 46 [D5] 47 [D6] 48 [D3] 49 [D9]
50 words
Words for child 0: [S2] [S3] [S9] [H9] [H6] [HA] [H8] [C6] [CK] [D8] [DQ] [D7] [D3]
Words for child 1: [SA] [S6] [S7] [S4] [H4] [HJ] [H7] [CQ] [C9] [CT] [DT] [D2] [D9]
Words for child 2: [SQ] [S5] [H2] [C7] [C5] [C8] [CA] [C4] [CJ] [C3] [DA] [D5]
Words for child 3: [S8] [SJ] [SK] [ST] [HK] [HT] [H5] [H3] [HQ] [C2] [D4] [D6]
Send [S2 S3 S9 H9 H6 HA H8 C6 CK D8 DQ D7 D3] to child 0
Child : 0, pid 89366:
parent: child 0 (PID 89366) played HA
Send [SA S6 S7 S4 H4 HJ H7 CQ C9 CT DT D2 D9] to child 1
Child : 1, pid 89367:
parent: child 1 (PID 89367) played S4
Send [SQ S5 H2 C7 C5 C8 CA C4 CJ C3 DA D5] to child 2
Child : 2, pid 89368:
parent: child 2 (PID 89368) played C5
Send [S8 SJ SK ST HK HT H5 H3 HQ C2 D4 D6] to child 3
Child : 3, pid 89369:
parent: child 3 (PID 89369) played D6
PID 89368 died with exit status 0x0000
PID 89367 died with exit status 0x0000
PID 89366 died with exit status 0x0000
PID 89369 died with exit status 0x0000

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-26
    • 2015-07-02
    • 2018-05-26
    • 1970-01-01
    • 2017-07-07
    • 2015-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多