2011-02-12 33 views
2

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

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; 
} 
+0

哈哈,这似乎是经典的“opertive系统”练习考试:D不是吗? – dynamic 2011-02-12 23:38:50

+0

更多的作业,我不明白哈哈。我不会问,除了我花了很多时间在网上搜索答案/其他人,并且无法得到答案。 – Mike 2011-02-13 00:01:03

+0

添加作业标签:) – 2011-04-16 15:35:55

回答

10

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


的代码更仔细,家长不写邮件给子进程,所以你有两倍多的管道,因为你需要 - 你只需要一个管道为每个子进程写回父母与。

你也不会open()已经打开管道的文件描述符......但你是如何得到代码编译?您必须缺少正确的标头(#include <fcntl.h>open(),并且没有启用足够的警告选项进行编译。

您提供的代码中未使用您的变量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()函数应该在一个缓冲区中传递,以及它的大小;代码应该尝试读取该缓冲区大小;它应该无效终止它接收的内容;它可以将指针返回到它传递的缓冲区;它应该错误地检查从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]); 
} 

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

1
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时,第二次迭代中的读取尝试将失败。

0

你能复制你的打开和关闭功能的代码吗?正如查尔斯所说,他们似乎不是标准的。

此外,在函数readers()中,当您尝试在主函数中读取此变量的内容时,返回指向局部变量的指针是错误的,因此您没有保证所指向的内存包含所需内容。如果你想返回一个缓冲区,你必须为它保留malloc的内存,使用一个外部变量(我的意思是一个全局变量)或返回一个指向内部(本地)静态变量的指针,因为(不像自动本地变量),所有这些变量存储在函数执行结束时都会持续存在。