我正在做OStep的家庭作业(job),并做了以下练习:

编写一个程序,创建两个子系统,并使用 pipe()系统调用.

下面是我如何理解我需要做的:为了连接第一个子进程的标准输出,我应该在父进程中使用pipe(pipe_name)创建管道,执行一个fork()创建第一个子进程,然后使用dup2( pipe_name[1],STDOUT_FILENO);更改标准输出流.之后,任何printf语句都会将文本发送到管道.然后我回到父进程,执行另一个fork()以创建第二个子进程,执行dup2( pipe_name[0],STDIN_FILENO);以更改输入.

这是正确的吗?如果是这样的话,第二个子元素怎么能读到管子里的东西?我想做

char words;
read(pipe_name[0], words, 1000);

但我的输出只有0,不是我之前打印的字符串.我的C语法有什么问题吗,或者它更基本(我只是在学习C,所以前者是一个明确的可能性).

以下是我的完整代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int
main(int argc, char *argv[])
{   int pipe_name[2];
    printf("hello world (pid:%d)\n", (int) getpid());
    if(pipe(pipe_name)<0){ printf("mission failed! We'll gettem next time...\n");
    } else {
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
        dup2( pipe_name[1],STDOUT_FILENO);
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    } else {
        // parent goes down this path (original process)
        wait(NULL);

        int rc2 = fork();

                if (rc2 < 0) {
                    // fork failed; exit
                    fprintf(stderr, "fork failed\n");
                    exit(1);
                } else if (rc2 == 0) {
                    
                    dup2( pipe_name[1],STDIN_FILENO);
                    char words;
                    read(STDIN_FILENO, words, 1000);
                    printf("My sibling says:\n    %d\n", words);
                } else { wait(NULL);
                    // parent goes down this path (original process)
                    printf("hello, I am parent of %d (pid:%d)\n",
                    rc2, (int) getpid());
                }
    }
    return 0;
    }}

并且输出是

hello world (pid:30421)
hello, I am child (pid:30422)
My sibling says:
    0
hello, I am parent of 30423 (pid:30421)

推荐答案

以下是影响代码的主要问题:

  • 如何将多达1000个字符读入一个字符

    char words; read(STDIN_FILENO, words, 1000);
    
  • 如果您读取字符,则需要将其格式化为字符串(%s,而不是%d).

  • 将管道的写入端复制到标准输入意味着读取操作将失败

    dup2(pipe_name[1],STDIN_FILENO);
    

    该1必须为0.

  • 在管道上写入的内容将不是以null结尾的字符串,而是一个字节array.你必须确保它是一个字符串.

最好确保管道未使用的端部及时关闭. 这并不是一个大问题,因为您只执行一次读取操作,而不是读取直到读取. 最好从一开始就养成良好的习惯.

此外,错误应该在stderr上报告,当出现错误时,程序应该以非零状态退出.

以下是解决这些问题的一些修订代码:

/* SO 7818-3904 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

static void wait_loop(void)
{
    int corpse;
    int status;

    while ((corpse = wait(&status)) != -1)
    {
        fprintf(stderr, "%d: PID %d exited with status 0x%.4X\n",
                (int)getpid(), corpse, status);
    }
}

int
main(void)
{
    int pipe_name[2];
    printf("hello world (pid:%d)\n", (int)getpid());
    if (pipe(pipe_name) < 0)
    {
        fprintf(stderr, "mission failed! We'll gettem next time...\n");
        exit(1);
    }

    int rc1 = fork();
    if (rc1 < 0)
    {
        fprintf(stderr, "fork 1 failed\n");
        exit(1);
    }
    else if (rc1 == 0)
    {
        printf("hello, I am child 1 (pid:%d)\n", (int)getpid());
        dup2(pipe_name[1], STDOUT_FILENO);
        close(pipe_name[0]);
        close(pipe_name[1]);
        printf("hello, I am child 1 (pid:%d)\n", (int)getpid());
    }
    else
    {
        wait_loop();    /* Not really necessary here */
        int rc2 = fork();
        if (rc2 < 0)
        {
            fprintf(stderr, "fork 2 failed\n");
            exit(1);
        }
        else if (rc2 == 0)
        {
            close(pipe_name[1]);
            printf("hello, I am child 2 (pid:%d)\n", (int)getpid());
            dup2(pipe_name[0], STDIN_FILENO);
            close(pipe_name[0]);
            char words[1000];
            int nbytes = read(STDIN_FILENO, words, sizeof(words));
            if (nbytes > 0)
            {
                words[nbytes] = '\0';
                printf("My sibling says:\n%d: [%s]\n", nbytes, words);
            }
            else
            {
                fprintf(stderr, "%d: Got value %d from read(): %d %s\n",
                        (int)getpid(), nbytes, errno, strerror(errno));
                exit(1);
            }
        }
        else
        {
            close(pipe_name[0]);
            close(pipe_name[1]);
            wait_loop();
            // parent goes down this path (original process)
            printf("%d: hello, I am parent of %d and %d\n",
                   (int)getpid(), rc1, rc2);
        }
    }
    return 0;
}

样品输出:

hello world (pid:23510)
hello, I am child 1 (pid:23511)
23510: PID 23511 exited with status 0x0000
hello, I am child 2 (pid:23512)
My sibling says:
32: [hello, I am child 1 (pid:23511)
]
23510: PID 23512 exited with status 0x0000
23510: hello, I am parent of 23511 and 23512

请注意,我把等待的代码放入一个函数中,并打印出返回的值—我认为wait(NULL)是一个不连贯的指示. 您的流程可能有它不知道的子项目(不太可能,但可能). 获取输出有助于调试.

第一个等待循环并不是真正必要的,尽管在这种情况下它没有什么坏处. 然而,如果子系统传输了足够多的数据(这些天,超过64 KiB,但POSIX只保证一个管道可以容纳4 KiB),那么早期等待循环将阻止读取器启动,写入器将永远不会完成,因为它正在等待某个进程清空管道,这不会发生. 这是一个僵局,需要避免.

C++相关问答推荐

理解C中的指针定义

了解一些CLIPS原语数据类型

C中是否有语法可以直接初始化一个常量文本常量数组的 struct 成员?

为什么可以在typedef之前使用typedef d struct 体?

S的这种管道实施有什么问题吗?

如何用c语言修改shadow文件hash部分(编程)?

我在反转双向链表时遇到问题

如何使这个While循环在新行上结束

S,在 struct 中创建匿名静态缓冲区的最佳方式是什么?

使用正则表达式获取字符串中标记的开始和结束

用C++高效解析HTTP请求的方法

为什么会导致分段故障?(C语言中的一个程序,统计文件中某个单词的出现次数)

生成一个半RNG,结果用C表示(无随机/随机)

正在try 理解C++中的`正在释放的指针未被分配‘错误

如何在MSVC中使用intSafe.h函数?

我正在使用c学习数据 struct ,在学习堆栈时,我试图将中缀转换为后缀,并编写了这段代码.代码未给出输出

未使用sem_open正确初始化信号量

在分配内存后使用指针是未定义的行为吗?

GCC认为这是一个VLA是对的吗?

为什么实现文件中的自由函数默认没有内部链接?