gpt4 book ai didi

c - C问题中的管道通信

转载 作者:太空狗 更新时间:2023-10-29 11:24:55 25 4
gpt4 key购买 nike

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

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

最佳答案

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

更仔细地查看代码,父级不会将消息写入子进程,因此您拥有所需管道的两倍-每个子进程只需要一个管道即可写回父级。

您还没有open()已经打开管道的文件描述符...但是如何获取代码进行编译?您必须缺少#include <fcntl.h>的正确 header (open()),并且在未启用足够的警告选项的情况下进行编译。

您显示的代码中未使用您的变量COOP和SIL。

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

还值得养成对每个可能失败的系统调用进行错误检查返回错误检查的习惯。有一些不会失败的-getpid()就是这样的一种。但是,由于失败的原因,I/O操作由于程序直接控制之外(或在程序控制范围内)之外的原因而臭名昭著,因此应检查写入是否成功。当您找回EBADF(错误的文件描述符)错误时,您知道有问题。
close()中的open()(和reader())也有类似的问题,另外还有另一个问题,您试图返回一个指向本地自动变量的指针-从来都不是一个好主意。同样,启用警告的功能良好的编译器(如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)。

关于c - C问题中的管道通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4981236/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com