#c
Вопрос:
У меня есть упражнение, в котором мне нужно взаимодействовать с программой на языке Си через канал.
У меня есть следующий источник, который я не могу изменить.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int number;
int answer;
number = rand() % 100;
printf("Print the double of the number %dn", number);
scanf("%d", amp;answer);
if(number * 2 == answer)
printf("Successn");
else
printf("Errorn");
}
Я попытался взаимодействовать с этой программой с помощью этого кода
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
pipe(STDIN_PIPE);
pipe(STDOUT_PIPE);
pid_t pid = fork();
if(pid == 0)
{
char *path = "/path/to/binary";
char *args[2];
args[0] = path;
args[1] = NULL;
close(STDIN_PIPE[1]);
close(STDOUT_PIPE[0]);
dup2(STDIN_PIPE[0], STDIN_FILENO);
dup2(STDOUT_PIPE[1], STDOUT_FILENO);
execve(path, args, env);
}
else
{
char buf[128];
close(STDIN_PIPE[0]);
close(STDOUT_PIPE[1]);
while(read(STDOUT_PIPE[0], buf, 1))
write(1, buf, 1);
}
}
Но когда я запускаю его, он попадает в бесконечный цикл, ничего не печатая.
Комментарии:
1. Ваша программа должна прочитать первый канал, чтобы получить вопрос от другой программы, и записать правильный ответ в другой канал, а затем снова прочитать первый канал, чтобы получить успех или неудачу от другой программы.
2. Да, я знаю, но для того, чтобы знать, я даже не могу прочитать ни одного символа.
3. Вы должны добавить некоторые проверки, чтобы убедиться, что трубы созданы правильно.
Ответ №1:
Я исправил ряд проблем в вашем коде, добавил множество проверок на ошибки и выполнил их так, чтобы конечная цель была достигнута.
В дочернем процессе srand()
необходимо вызвать для инициализации генератор случайных чисел, иначе вы всегда получите одно и то же значение.
В дочернем процессе вы должны flush(stdout)
после печати вопроса сделать так, чтобы он действительно был записан в канал.
И, наконец, scanf()
необходимо проверить возвращаемое значение.
В основном процессе я добавил множество проверок на ошибки. И я пишу readLine
функцию, чтобы — угадайте, что — прочитать строку из трубы. Строка заканчивается символом конца строки n
.
Еще есть место для некоторых улучшений…
Я протестировал свой код с помощью кода Visual Studio, настроенного для gcc и работающего под Ubuntu 20.04.
Вот источник дочернего процесса:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int number;
int answer;
time_t t;
srand((unsigned)time(amp;t));
number = rand() % 100;
printf("Print the double of the number %dn", number);
fflush(stdout);
int n = scanf("%d", amp;answer);
if (n != 1) {
printf("Invalid inputn");
return 1;
}
if ((number * 2) == answer) {
printf("Successn");
return 0;
}
printf("Error %d is not 2 * %dn", answer, number);
return 1;
}
И вот основной источник процесса:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int readLine(int fd, char *buf, int bufSize);
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
if (pipe(STDIN_PIPE))
{
perror("pipe(STDIN_PIPE)");
return 1;
}
if (pipe(STDOUT_PIPE)) {
perror("pipe(STDOUT_PIPE)");
return 1;
}
pid_t pid = fork();
if (pid == 0)
{
char *path = "../Child/Child"; // Path to child process, adapt to your environment
char *args[2];
args[0] = path;
args[1] = NULL;
if (dup2(STDIN_PIPE[0], STDIN_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
if (dup2(STDOUT_PIPE[1], STDOUT_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
if (execve(path, args, env) == -1) {
perror("execve failed");
return 1;
}
}
else
{
char buf[128];
int bufSize = sizeof(buf) / sizeof(buf[0]);
int i;
// Read the question asked by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.n");
return 1;
}
// We receive something like "Print the double of the number 83"
printf("Child process question is "%s".n", buf);
// Extract the number at end of string
i = strlen(buf) - 1;
while ((i >= 0) amp;amp; isdigit(buf[i]))
i--;
int value = atoi(buf i 1);
// Write our answer to write end of STDIN_PIPE
char answer[128];
int answerSize = sizeof(answer) / sizeof(answer[0]);
int answerLen = snprintf(answer, answerSize, "%dn", value * 2);
printf("Our answer is "%d".n", value * 2);
if (write(STDIN_PIPE[1], answer, answerLen) != answerLen) {
printf("write failed.n");
return 1;
}
// Read the response (success or failure) sent by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.n");
return 1;
}
if (strcasecmp(buf, "Success") == 0)
printf("Child process returned success.n");
else
printf("Child process returned failure.n");
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
}
return 0;
}
// Read a line from file descriptor
// A line is any characters until n is received or EOF
// n is not kept
// Return the number of characters read or <0 if error:
// -1 => Input buffer overflow
// -2 => read() failed and errno has the error
int readLine(int fd, char *buf, int bufSize)
{
int i = 0;
while (1)
{
// Check if enough room in the buffer
if (i >= bufSize) {
printf("Input buffer overflown");
return -1;
}
// Read one character from the pipe
ssize_t n = read(fd, buf i, 1);
if (n == -1)
{
perror("read() failed");
return -2;
}
if (n == 0)
{
// EOF received, that's OK
return i;
}
// NUL terminate the buffer
buf[i 1] = 0;
// Check for end of line character
if (buf[i] == 'n') {
buf[i] = 0; // Remove ending n
return i;
}
i ;
}
}