管道通信中的C问题

Mik*_*ike 2 c linux pipe

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

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;
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ler 10

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


更仔细地查看代码,父进程不会向子进程写入消息,因此您需要的管道数量是所需数量的两倍 - 每个子进程只需要一个管道就可以回写给父进程.

您还没有open()打开管道的文件描述符......但是您是如何获得编译的代码的?如果没有启用足够的警告选项,您必须缺少正确的标题(#include <fcntl.h>)open()和编译.

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


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

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

你有close()(和open())in 类似的问题reader(),加上你试图返回指向本地自动变量的指针的额外问题 - 这不是一个好主意.同样,一个体面的编译器(如GCC)启用警告会告诉你这些事情.我用这个命令来编译你的程序:

gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
    pipe.c -o pipe
Run Code Online (Sandbox Code Playgroud)

您的子进程总是会生成相同的(伪)随机数序列,这不是很令人兴奋.您可能应该使用以下内容:

srand(getpid());
Run Code Online (Sandbox Code Playgroud)

确保他们得到不同的序列.


您的reader()功能既不够热情,也不太热衷于阅读数据.您一次只读取一个字节,但是然后循环以累积单个字节,因此代码等待所有10个结果都知道,然后立即吐出所有内容.由于32位整数可以存储多达1,111,111,111的数字而没有问题,因此atoi()在第一次迭代时,您只能从调用中返回一个数字,这不是您想要的.

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

因此,您的reader()函数应该在缓冲区中传递以及其大小; 代码应该尝试读取缓冲区大小; 它应该null终止它收到的内容; 它可以将指针返回到它传递的缓冲区; 它应该错误检查返回的值read().

两个子进程的代码基本相同 - 您应该使用适当的参数化函数,而不是两次写出代码.


总而言之,你最终会得到这样的东西(在MacOS X 10.6.6和GCC 4.5.2上对我来说很好):

#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]);
}
Run Code Online (Sandbox Code Playgroud)

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