【问题标题】:Pipe communication in C problemsC问题中的管道通信
【发布时间】:2011-02-12 23:12:11
【问题描述】:

我正在尝试编写一些代码,它使用管道在父进程和它的子进程之间进行通信。但是,我的管道似乎在我第一次使用后就放弃了(也就是说,它在第一次使用管道后停止工作)。我不太确定如何解决这个问题,任何帮助将不胜感激。我也知道我在这方面使用的一些编码实践并不是很理想(主要是使用睡眠)。

const int READ = 0;
const int WRITE = 1;
char* COOP = "Criminal cooperates\n";
char* SIL = "Criminal doesn't talk\n";

char* reader(int);
void writer(int, char *c);

int main()
{       
    int c1pipe1[2];
    int c1pipe2[2];
    int c2pipe1[2];
    int c2pipe2[2];
    int c1sentence = 0;
    int c2sentence = 0;
    int r;
    int c;
    pipe(c1pipe1);
    pipe(c1pipe2);
    pipe(c2pipe1);
    pipe(c2pipe2);
    int C2;
    int C1 = fork();
    if(C1 > 0)
        C2 = fork();
    if(C1 < 0 || C2 < 0) //error
    {
        perror("fork() failed");
        exit(1);
    }

    else if(C1 == 0)
    {
        close(c1pipe1[WRITE]);
        close(c1pipe2[READ]);
        for(c = 0; c < 10; c++)
        {
            r = rand();
            //printf("C1 rand = %d\n", r%2);
            if(r % 2 == 1)
                writer(c1pipe2[WRITE], "1");
            else
                writer(c1pipe2[WRITE], "0");
            sleep(1);
        }

        exit(0);
    }
    else if(C2 == 0)
    {
        close(c2pipe1[WRITE]);
        close(c2pipe2[READ]);
        for(c = 0; c < 10; c++)
        {
            r = rand();
            //printf("C2 rand = %d\n", r%2);
            if(r % 2 == 1)
                writer(c2pipe2[WRITE], "1");
            else
                writer(c2pipe2[WRITE], "0");
            sleep(1);
        }

        exit(0);
    }
    else //parent
    {
        int buff1; //stores choice of c1
        int buff2; //stores choice of c2
        close(c1pipe1[READ]);
        close(c1pipe2[WRITE]);
        close(c2pipe1[READ]);
        close(c2pipe2[WRITE]);
        for(c = 0; c< 10; c++)
        {
            buff1 = atoi(reader(c1pipe2[READ]));
            buff2 = atoi(reader(c2pipe2[READ]));
            printf("C1's \(%d)\ choice trial %d : %d\n", C1, c+1, buff1);
            printf("C2's \(%d)\ choice trial %d : %d\n", C2, c+1, buff2);
            if(buff1 && buff2) //c1 and c2 cooperate with police
            {
                    c1sentence = c1sentence + 6;
                    c2sentence = c2sentence + 6;
            }
            else if(buff1 || buff2) // one cooperates, one is silent
            {
                if(buff1) // if c1 cooperates and c2 is silent
                {
                    c1sentence = c1sentence + 0;
                    c2sentence = c2sentence + 10;
                }
                else // if c2 cooperates and c1 is silent
                {
                    c1sentence = c1sentence + 10;
                    c2sentence = c2sentence + 0;
                }
            }
            else if(!(buff1 && buff2)) //both c1 and c2 are silent
            {
                c1sentence = c1sentence + 1;
                c2sentence = c2sentence + 1;
            }
            sleep(1);


        }       
        printf("C1 is in jail for %d years total\n", c1sentence);
        printf("C2 is in jail for %d years total\n", c2sentence);
        exit(0);
    }
    exit(0);
}

void writer(int pipe_write_fd, char *c) 
{
    open(pipe_write_fd);
    char* choice = c;
    // Write to the pipe
    write(pipe_write_fd, choice, strlen(choice));
    // Close the pipe
    // (Sends 'end of file' to reader)
    close(pipe_write_fd);
}

char* reader(int pipe_read_fd) 
{
    open(pipe_read_fd);
    // Allocate buffer to store
    // result of read
    int buffer_size = 1024;
    char buffer[buffer_size];

    // Keep reading until we exhaust
    // buffer or reach end of file
    int i = 0;
    while (i < buffer_size
           && read(pipe_read_fd, &buffer[i], 1) > 0)
    { i++; }

    if (i < buffer_size) {
        // Add null termination
        buffer[i] = '\0';
    } else {
        // We exhausted buffer
        fprintf(stderr, "Warning: buffer full.\n");
        buffer[buffer_size-1] = '\0';
    }

    //printf("%s", buffer);

    // Close the pipe
    close(pipe_read_fd);
    return buffer;
}

【问题讨论】:

    标签: c linux pipe


    【解决方案1】:

    您需要关闭更多管道。子进程必须关闭它们不使用的每个管道文件描述符。你有 8 个管道文件描述符;每个子进程必须关闭其中的 6 个 - 至少!建议您不要像以前那样预先创建所有管道 - 控制事物并关闭所有正确的描述符是很复杂的。


    更仔细地查看代码,父进程不会将消息写入子进程,因此您需要的管道数量是您需要的两倍 - 每个子进程只需要一个管道来写回父进程。

    你也没有open() 已经打开文件描述符到管道......但是你是如何让代码编译的?您必须缺少 open() 的正确标头 (#include &lt;fcntl.h&gt;),并且在未启用足够警告选项的情况下进行编译。

    您的变量 COOP 和 SIL 在提供的代码中未使用。


    您的writer() 函数不仅错误地尝试打开一个已经关闭的文件描述符,而且还关闭了它,这意味着在第一个消息之后无法发回额外的消息。您应该只在完成后关闭文件描述符 - 在每个子程序的主程序中循环之后。这就是您只看到一条消息的原因。

    养成对每个可能失败的系统调用的返回进行错误检查的习惯也是值得的。有一些不能失败 - getpid() 就是这样一个。但是 I/O 操作因程序直接控制之外的原因(或者,在这种情况下,在程序的控制范围内)失败而臭名昭著,因此您应该检查写入是否成功。当您返回 EBADF - 错误的文件描述符 - 错误时,您就知道发生了什么事。

    您在reader() 中与close()(和open())有类似的问题,另外还有一个问题是您尝试返回指向本地自动变量的指针——这从来都不是一个好主意。同样,启用警告的体面的编译器(如 GCC)会告诉你这些事情。我用这个命令来编译你的程序:

    gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
        pipe.c -o pipe
    

    您的子进程总是会生成相同的(伪)随机数序列,这不是很令人兴奋。你可能应该使用类似的东西:

    srand(getpid());
    

    以确保它们得到不同的序列。


    您的reader() 函数既不够热情,又对阅读数据过于热情。您一次读取一个字节,然后循环以累积单个字节,因此代码等待所有 10 个结果都知道,然后立即将所有内容吐出。由于 32 位整数可以毫无问题地存储最多 1,111,111,111 的数字,因此在第一次迭代时,您对atoi() 的调用只会返回一个数字,这并不是您想要的。

    管道上的读取和写入是原子的 - 从某种意义上说,如果写入进程写入 6 个字节,而读取进程尝试读取超过 6 个字节,那么单次读取将返回 6 个字节的数据包,即使管道中有其他字节等待读取;这些额外的字节将在后续调用 read() 时返回。

    所以,您的reader() 函数应该连同它的大小一起传递到一个缓冲区中以供使用;代码应尝试读取该缓冲区大小;它应该 null 终止它收到的内容;它可以返回指向它传递的缓冲区的指针;它应该错误检查来自read()的返回值。

    两个子进程的代码本质上是相同的——你应该使用一个适当的参数化函数而不是写出两次代码。


    将所有内容放在一起,您最终会得到这样的结果(在带有 GCC 4.5.2 的 MacOS X 10.6.6 上对我来说效果很好):

    #include <errno.h>
    #include <string.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdarg.h>
    #include <stdlib.h>
    
    const int READ = 0;
    const int WRITE = 1;
    
    static char* reader(int fd, char *buffer, size_t bufsiz);
    static void writer(int fd, const char *c);
    static void child_process(int *my_pipe, int *his_pipe);
    
    static void err_exit(const char *fmt, ...)
    {
        va_list args;
        int errnum = errno;
        va_start(args, fmt);
        vfprintf(stderr, fmt, args);
        va_end(args);
        if (errnum != 0)
            fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
        exit(1);
    }
    
    int main(void)
    {       
        int c1pipe[2];
        int c2pipe[2];
        int c1sentence = 0;
        int c2sentence = 0;
        int c;
    
        if (pipe(c1pipe) != 0 || pipe(c2pipe) != 0)
            err_exit("Failed to open a pipe\n");
    
        int C2 = 0;
        int C1 = fork();
        if (C1 > 0)
            C2 = fork();
    
        if (C1 < 0 || C2 < 0) //error
            err_exit("fork() failed\n");
        else if (C1 == 0)
            child_process(c1pipe, c2pipe);
        else if (C2 == 0)
            child_process(c2pipe, c1pipe);
        else //parent
        {
            int choice1; //stores choice of c1
            int choice2; //stores choice of c2
            char buffer1[BUFSIZ];
            char buffer2[BUFSIZ];
            close(c1pipe[WRITE]);
            close(c2pipe[WRITE]);
            for (c = 0; c< 10; c++)
            {
                choice1 = atoi(reader(c1pipe[READ], buffer1, sizeof(buffer1)));
                choice2 = atoi(reader(c2pipe[READ], buffer2, sizeof(buffer1)));
                printf("C1's (%d) choice trial %d : %d\n", C1, c+1, choice1);
                printf("C2's (%d) choice trial %d : %d\n", C2, c+1, choice2);
                if (choice1 && choice2) //c1 and c2 cooperate with police
                {
                        c1sentence = c1sentence + 6;
                        c2sentence = c2sentence + 6;
                }
                else if (!(choice1 && choice2)) //both c1 and c2 are silent
                {
                    c1sentence = c1sentence + 1;
                    c2sentence = c2sentence + 1;
                }
                else if (choice1) // if c1 cooperates and c2 is silent
                {
                    c1sentence = c1sentence + 0;
                    c2sentence = c2sentence + 10;
                }
                else // if c2 cooperates and c1 is silent
                {
                    c1sentence = c1sentence + 10;
                    c2sentence = c2sentence + 0;
                }
            }       
            printf("C1 is in jail for %d years total\n", c1sentence);
            printf("C2 is in jail for %d years total\n", c2sentence);
        }
        return(0);
    }
    
    static void writer(int pipe_write_fd, const char *c) 
    {
        int len = strlen(c);
        if (write(pipe_write_fd, c, len) != len)
            err_exit("Write failed\n");
    }
    
    static char* reader(int pipe_read_fd, char *buffer, size_t bufsiz) 
    {
        int i = read(pipe_read_fd, buffer, bufsiz-1);
        if (i < 0)
            err_exit("Read failed\n");
        buffer[i] = '\0';
        return buffer;
    }
    
    static void child_process(int *my_pipe, int *his_pipe)
    {
        int c;
        srand(getpid());
        close(my_pipe[READ]);
        close(his_pipe[READ]);
        close(his_pipe[WRITE]);
        for (c = 0; c < 10; c++)
        {
            writer(my_pipe[WRITE], ((rand() % 2) == 1) ? "1" : "0");
            sleep(1);
        }
        close(my_pipe[WRITE]);
    }
    

    注意错误例程如何及早捕获errno - 以避免损坏它。这是使用全局变量的危险之一;当你调用一个函数时,它们可能会改变。可以避免时不要使用它们(但请注意,一般情况下,您不能完全避免使用 errno)。

    【讨论】:

    • 啊。我实际上有 4 个管道文件描述符,我完全删除了它们,然后我关闭了未使用的管道(所以每个孩子有 3 个关闭的管道,因为现在总共有 4 个管道末端),但我仍然遇到同样的问题之前,我的管道在第一次之后似乎没有发送任何内容。
    【解决方案2】:
    void writer(int pipe_write_fd, char *c) 
    {
        open(pipe_write_fd);
        char* choice = c;
        // Write to the pipe
        write(pipe_write_fd, choice, strlen(choice));
        // Close the pipe
        // (Sends 'end of file' to reader)
        close(pipe_write_fd);
    }
    

    我不确定您正在尝试使用哪个函数open,但通常的函数采用文件名并返回文件描述符。无论如何,您都在丢弃返回值,所以我认为这无关紧要。

    很明显,您在第一次写入后立即close 管道,因此下一次写入将失败是“正确的”;管道已关闭。

    如果你解决了这个问题,那么下一个问题是reader 将在关闭读取管道之前,一次一个字节,所有可用输入 - 最多 1024 个字节。由于reader在循环中被调用,第二次迭代的读取尝试将失败。

    【讨论】:

    • 是的,我实际上认为这可能是问题所在,并将管道更改为在子/父进程结束时关闭,但这并没有改变我遇到的问题。
    猜你喜欢
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多