gpt4 book ai didi

c - c 中的管道,2 个 fork 与 main 对话

转载 作者:行者123 更新时间:2023-12-03 09:47:49 25 4
gpt4 key购买 nike

好吧,简短的故事是:
我有一个需要做 X 的程序,它做了 0.25X。我使用 2 个 fork 和 4 个管道,我不知道如何调试它。 (在 linux env 中使用 eclipse c/c++)。

很长的故事:
我有一个程序需要从包含整数对的文本文件中计算 gcd(最大公约数)。这个程序有一个父亲(主)和两个 child (叉)需要通过管道与父亲交谈。 (每个 child 2 个管子。)
当我在 ubuntu 中编译并运行程序时,我没有收到任何错误,但程序没有完成其任务。我不知道它在哪里/为什么坏了。我该如何调试?我在 eclipse c/c++ 中编码,调试器无法处理 fork 。当我调试时,它从文件中读取所有数字(不计算 gcd)但是当我在 ubuntu 终端中运行时,它只读取第一行并中断。这是完整的代码:

int main(int argc, char **argv) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "‫‪usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}

int pfd_child1_r[2], pfd_child1_w[2], pfd_child2_r[2], pfd_child2_w[2];
if (pipe(pfd_child1_r) == -1 || pipe(pfd_child1_w) == -1
|| pipe(pfd_child2_r) == -1 || pipe(pfd_child2_w) == -1) {
perror("cannot pipe()");
return EXIT_FAILURE;
}

createChilds(pfd_child1_r, pfd_child1_w, pfd_child2_r, pfd_child2_w);

FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}

char line[100];
char *token;
int numbers[2], num, line_count = 1, counter = 0, result = 0;

while (fgets(line, sizeof(line), fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "‫‪illegal‬‬ ‫‪input‬‬ at line %d\n",
line_count);
return EXIT_FAILURE;
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
counter = 0;
if (line_count % 2 == 0) { // use first child
write(pfd_child1_w[1], &numbers[0], sizeof(int));
write(pfd_child1_w[1], &numbers[1], sizeof(int));
} else { // use second child
write(pfd_child2_w[1], &numbers[0], sizeof(int));
write(pfd_child2_w[1], &numbers[1], sizeof(int));
}

if (line_count > 1) { // after first run alternate to get result
if (line_count % 2 == 0) { // read from second child
read(pfd_child2_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else { // read from first child
read(pfd_child1_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
}

line_count++;
}

fclose(fp);
return EXIT_SUCCESS;
}

void createChilds(int pfd_child1_r[2], int pfd_child1_w[2], int pfd_child2_r[2],

int pfd_child2_w[2]) {

switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);

case 0: /* First child: */
if (close(pfd_child1_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}

if (close(pfd_child1_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}

/* Duplicate stdout on write end of pipe; close duplicated descriptor */

if (pfd_child1_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child1_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}

/* Duplicate stdin on read end of pipe; close duplicated descriptor */

if (pfd_child1_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child1_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);

default: /* Parent go to next child */
break;
}

switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);

case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd_child2_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}

if (close(pfd_child2_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}

/* Duplicate stdout on write end of pipe; close duplicated descriptor */

if (pfd_child2_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child2_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}

/* Duplicate stdin on read end of pipe; close duplicated descriptor */

if (pfd_child2_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child2_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);

default: /* Parent falls through */
break;
}

/* Parent closes unused file descriptors for pipe */

if (close(pfd_child1_r[1]) == -1 || close(pfd_child1_w[0]) == -1
|| close(pfd_child2_r[1]) == -1 || close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}

第二个文件是 gcd 文件,我还没有完成它,应该继续获取数字的循环不存在。但我只想让第一行正常工作,然后我会修复其余部分。
int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}

int main(int argc, char **argv) {

int n1, n2, result;
if (scanf("%d %d", &n1,&n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2,n1);
printf("%d", result);
}

最佳答案

如何调试

一种简单的调试方法总是添加 fprintf(stderr, "...")对子程序的声明。然后您可以运行该程序并查看子进程在做什么。

转移值

由于您重定向 stdin 和 stdout 并在计算 gcd 的 v1_child 程序中使用 sscanf/printf,我假设您想将值作为字符串传输。

一种简单的方法是使用 fprintf 将整数写入格式化字符串。您可以使用 fdopen 将流关联到现有的管道文件描述符。

因此,您必须将数字从字符串转换为字符串。

可变长度数据和缓冲 I/O

如果您使用字符串来传输值,则每对值都有一个可变长度。通常,在 C 程序中使用换行符来表示完整的输入记录。

读/写整行的另一个原因是读/写调用也只能传输部分字节数。因此,您必须知道输入记录何时完成。另一种方法是使用二进制格式,它会自动表示具有固定长度的格式。

通过使用流处理缓冲 I/O,使用 fflush 可以确保所有缓冲数据都通过流的底层写入函数写入。

函数

可以将特征分成几个功能,使流程更容易理解。

可能的改进

这或许已经是一个开始。

另一个可能的改进可能是使用 strtol 代替 atoi,因为 atoi 不执行错误检查。类似的 sscanf 不会报告转换错误(例如行尾的非数字字符),至少我们查看分配的输入项的数量。

大概还有可能提高代码的可读性。

使用 waitpid 可以在父级中检查子级的退出状态代码。

程序

您的代码在上面提到的几点中稍作修改,然后可能如下所示:

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

void create_pipe(int *);
void close_fd(int);
int child(const int *, const int *);
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count);
void write_to_child(FILE *fp, const int *numbers);
int read_from_child(FILE *fp);


int main(int argc, char *argv[]) {

if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "‫‪usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}

int pfd_child1_r[2];
int pfd_child1_w[2];
int pfd_child2_r[2];
int pfd_child2_w[2];

create_pipe(pfd_child1_r);
create_pipe(pfd_child1_w);
create_pipe(pfd_child2_r);
create_pipe(pfd_child2_w);

pid_t pid1 = fork();

if (pid1 == 0) { //child 1
close_fd(pfd_child2_r[0]);
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);
close_fd(pfd_child2_w[1]);
return child(pfd_child1_r, pfd_child1_w);
} else if (pid1 > 0) {
close_fd(pfd_child1_r[1]);
close_fd(pfd_child1_w[0]);

pid_t pid2 = fork();
if (pid2 == 0) { //child 2
close_fd(pfd_child1_r[0]);
close_fd(pfd_child1_w[1]);
return child(pfd_child2_r, pfd_child2_w);
} else if (pid2 > 0) {
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);

FILE *fp_child1_w = fdopen(pfd_child1_w[1], "w");
FILE *fp_child2_w = fdopen(pfd_child2_w[1], "w");
FILE *fp_child1_r = fdopen(pfd_child1_r[0], "r");
FILE *fp_child2_r = fdopen(pfd_child2_r[0], "r");

if (!fp_child1_w || !fp_child2_w || !fp_child1_r || !fp_child2_r) {
perror("fdopen() failed");
return EXIT_FAILURE;
}

FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}

char line[100];
int numbers[2], line_count = 0;
while (read_input_line(fp, line, sizeof(line), numbers, line_count) == 2) {
if (line_count % 2 == 0) {
write_to_child(fp_child1_w, numbers);
} else {
write_to_child(fp_child2_w, numbers);
}

if (line_count % 2 == 0) {
int result = read_from_child(fp_child1_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else {
int result = read_from_child(fp_child2_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
line_count++;
}

//fclose closes also associated file descriptor
fclose(fp_child1_w);
fclose(fp_child2_w);
fclose(fp_child1_r);
fclose(fp_child2_r);

fclose(fp);
return EXIT_SUCCESS;
} else {
perror("second fork failed");
return EXIT_FAILURE;
}
} else {
perror("first fork failed");
return EXIT_FAILURE;
}
}


int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count) {
char *token;
int num, counter = 0;

line[0] = '\0';
if (fgets(line, max, fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "‫‪illegal‬‬ ‫‪input‬‬ at line %d\n", line_count + 1);
exit(EXIT_FAILURE);
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
}
return counter;
}

int read_from_child(FILE *fp) {
char buf[128];
int result = -1;
if (fgets(buf, sizeof(buf), fp)) {
if (sscanf(buf, "%d", &result) == 1)
return result;
}
return -1;
}

void write_to_child(FILE *fp, const int *numbers) {
fprintf(fp, "%d %d\n", numbers[0], numbers[1]);
fflush(fp);
}

int child(const int *pfd_child_r, const int *pfd_child_w) {
dup2(pfd_child_r[1], STDOUT_FILENO);
dup2(pfd_child_w[0], STDIN_FILENO);
close_fd(pfd_child_r[0]);
close_fd(pfd_child_r[1]);
close_fd(pfd_child_w[0]);
close_fd(pfd_child_w[1]);
execlp("./v1_child", "./v1_child", NULL);
fprintf(stderr, "execution of v1_child failed\n");
exit(EXIT_FAILURE);
}

void create_pipe(int *fd) {
if (pipe(fd) == -1) {
perror("cannot pipe()");
exit(EXIT_FAILURE);
}
}

void close_fd(int fd) {
if (close(fd) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}

相应的 v1_child.c 可能如下所示:
#include <stdio.h>
#include <stdlib.h>

int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}

int main(void) {

int n1, n2, result;
char buf[128];
while(fgets(buf, sizeof(buf), stdin)) {
if (sscanf(buf, "%d %d", &n1, &n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2, n1);
printf("%d\n", result);
fflush(stdout);
}
return EXIT_SUCCESS;
}

测试

随着输入
5 25
49 14
64 462
1155 84

输出将是
5 25        gcd: 5
49 14 gcd: 7
64 462 gcd: 2
1155 84 gcd: 21

关于c - c 中的管道,2 个 fork 与 main 对话,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61193226/

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