#c #pipe #fork
#c #канал #форк
Вопрос:
Я пытаюсь понять, как работают каналы и разветвление. Итак, я написал простую программу, в которой родительский процесс отправляет сообщение дочернему процессу (который отлично работает). Но если я попытаюсь отправить сообщение обратно из дочернего процесса, добавив прокомментированный код, он перестанет работать. И выполнение программы останавливается после вывода «Родительское отправлено: привет».
int main() {
int child_to_parent[2];
int parent_to_child[2];
pipe(child_to_parent);
pipe(parent_to_child);
pid_t id = fork();
if (id == 0) {
close(parent_to_child[1]);
close(child_to_parent[0]);
FILE* out = fdopen(child_to_parent[1], "w");
FILE* in = fdopen(parent_to_child[0], "r");
char msg[6];
fscanf(in ,"%s", msg);
printf("Child got: %sn", msg);
/*
fprintf(out, "hi ");
printf("Child sent: hin");
*/
} else {
close(parent_to_child[0]);
close(child_to_parent[1]);
FILE* in = fdopen(child_to_parent[0], "r");
FILE* out = fdopen(parent_to_child[1], "w");
fprintf(out, "hello");
printf("Parent sent: hellon");
/*
char msg[3];
fscanf(in, "%s", msg);
printf("Parent got: %sn", msg);
*/
}
}
И я не могу понять, почему. Что меня больше всего смущает, так это то, почему дочерний процесс не может даже получить сообщение после того, как я изменил код. Кто-нибудь, пожалуйста, может сказать мне, что не так, или направить меня в правильном направлении?
Комментарии:
1. Вероятно, из-за буферизации. Я рекомендую не смешивать stdio /
FILE
с файловыми дескрипторами / каналами.2. Вам нужно удалить выходные данные из родительского процесса или закрыть поток от родительского к дочернему. Выходные данные будут полностью буферизованы, и вы не записываете полный буфер.
3. Да — попробуйте поместить
fflush(0);
после записи родительского процесса, но до того, как он будет прочитан.4. если вы хотите использовать
fscanf("%s", ...)
, вам нужно отправить разделитель (например, пробел или n) после слова, чтобы не блокировать fscanf и, конечно, прочитать символ разделителя после, fflush необходим после fwrite . Я ввел в свой ответ 3 способа чтения / записи, затем fread / fwrite (amp; fflush), затем, наконец, небольшое изменение в вашем коде с дополнительным разделителем (amp; fflush)
Ответ №1:
Первое решение, использующее чтение / запись для обмена любым количеством сообщений
Здесь я указываю конец каждого буфера для чтения с помощью n :
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void rd(int fd, int child)
{
char c;
int first = 1;
do {
if (!read(fd, amp;c, 1))
break;
if (first) {
printf("%s got:", (child) ? "Child" : "Parent");
first = 0;
}
putchar(c);
} while (c != 'n');
}
void wr(int fd, const char * msg, int child)
{
write(fd, msg, strlen(msg));
printf("%s sent:%s", (child) ? "Child" : "Parent", msg);
}
int main() {
int child_to_parent[2];
int parent_to_child[2];
pipe(child_to_parent);
pipe(parent_to_child);
pid_t id = fork();
if (id == 0) {
close(parent_to_child[1]);
close(child_to_parent[0]);
rd(parent_to_child[0], 1);
wr(child_to_parent[1], "hin", 1);
rd(parent_to_child[0], 1);
wr(child_to_parent[1], "fine, and you ?n", 1);
rd(parent_to_child[0], 1);
} else {
close(parent_to_child[0]);
close(child_to_parent[1]);
wr(parent_to_child[1], "hellon", 0);
rd(child_to_parent[0], 0);
wr(parent_to_child[1], "how are you ?n", 0);
rd(child_to_parent[0], 0);
wr(parent_to_child[1], "fine toon", 0);
}
}
Компиляция и выполнение :
pi@raspberrypi:/tmp $ gcc -pedantic -Wextra p.c
pi@raspberrypi:/tmp $ ./a.out
Parent sent:hello
Child got:hello
Child sent:hi
Parent got:hi
Parent sent:how are you ?
Child got:how are you ?
Child sent:fine, and you ?
Parent got:fine, and you ?
Parent sent:fine too
Child got:fine too
Также можно использовать fread/fwrite, fflush необходим после fwrite, чтобы не быть заблокированным. К счастью, нет необходимости закрывать канал после отправки, чтобы иметь возможность читать и отвечать, иначе можно обменять только одно сообщение. Я все еще использую n для указания конца отправленного буфера :
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void rd(FILE * fd, int child)
{
char c;
int first = 1;
do {
if (!fread(amp;c, 1, 1, fd))
break;
if (first) {
printf("%s got:", (child) ? "Child" : "Parent");
first = 0;
}
putchar(c);
} while (c != 'n');
}
void wr(FILE * fd, const char * msg, int child)
{
fwrite(msg, strlen(msg), 1, fd);
fflush(fd);
printf("%s sent:%s", (child) ? "Child" : "Parent", msg);
}
int main() {
int child_to_parent[2];
int parent_to_child[2];
pipe(child_to_parent);
pipe(parent_to_child);
pid_t id = fork();
if (id == 0) {
close(parent_to_child[1]);
close(child_to_parent[0]);
FILE* out = fdopen(child_to_parent[1], "w");
FILE* in = fdopen(parent_to_child[0], "r");
rd(in, 1);
wr(out, "hin", 1);
rd(in, 1);
wr(out, "fine, and you ?n", 1);
rd(in, 1);
} else {
close(parent_to_child[0]);
close(child_to_parent[1]);
FILE* in = fdopen(child_to_parent[0], "r");
FILE* out = fdopen(parent_to_child[1], "w");
wr(out, "hellon", 0);
rd(in, 0);
wr(out, "how are you ?n", 0);
rd(in, 0);
wr(out, "fine toon", 0);
}
}
Компиляция и выполнение:
pi@raspberrypi:/tmp $ gcc -pedantic -Wextra pp.c
pi@raspberrypi:/tmp $ ./a.out
Parent sent:hello
Child got:hello
Child sent:hi
Parent got:hi
Parent sent:how are you ?
Child got:how are you ?
Child sent:fine, and you ?
Parent got:fine, and you ?
Parent sent:fine too
Child got:fine too
И, наконец, если вы хотите использовать, fscanf("%s", ...)
вам нужно отправить разделитель (например, пробел или n) после слова, чтобы не блокировать fscanf и, конечно, также прочитать символ разделителя, fflush необходим после fwrite.
Если я немного изменю вашу программу :
#include <stdio.h>
#include <unistd.h>
int main() {
int child_to_parent[2];
int parent_to_child[2];
pipe(child_to_parent);
pipe(parent_to_child);
pid_t id = fork();
if (id == 0) {
close(parent_to_child[1]);
close(child_to_parent[0]);
FILE* out = fdopen(child_to_parent[1], "w");
FILE* in = fdopen(parent_to_child[0], "r");
char msg[16], c;
fscanf(in ,"%s%c", msg, amp;c);
printf("Child got: %sn", msg);
fprintf(out, "hi ");
fflush(out);
printf("Child sent: hin");
fscanf(in ,"%s%c", msg, amp;c);
printf("Child got: %sn", msg);
fprintf(out, "fine,you? ");
fflush(out);
printf("Child sent: fine,you?n");
fscanf(in ,"%s%c", msg, amp;c);
printf("Child got: %sn", msg);
} else {
close(parent_to_child[0]);
close(child_to_parent[1]);
FILE* in = fdopen(child_to_parent[0], "r");
FILE* out = fdopen(parent_to_child[1], "w");
fprintf(out, "hellon");
fflush(out);
printf("Parent sent: hellon");
char msg[16], c;
fscanf(in, "%s%c", msg, amp;c);
printf("Parent got: %sn", msg);
fprintf(out, "how-are-you? ");
fflush(out);
printf("Parent sent: how-are-you?n");
fscanf(in, "%s%c", msg, amp;c);
printf("Parent got: %sn", msg);
fprintf(out, "fine-too ");
fflush(out);
printf("Parent sent: fine-toon");
}
}
Компиляция и выполнение :
pi@raspberrypi:/tmp $ gcc -pedantic -Wextra ppp.c
pi@raspberrypi:/tmp $ ./a.out
Parent sent: hello
Child got: hello
Child sent: hi
Parent got: hi
Parent sent: how-are-you?
Child got: how-are-you?
Child sent: fine,you?
Parent got: fine,you?
Parent sent: fine-too
Child got: fine-too
Ответ №2:
fscanf
при использовании isspace
выполняется чтение до тех пор, пока не будет найден какой-либо пробельный символ (заданный %s
), но вы его не отправляете. So fscanf
никогда не возвращается, потому что он ожидает пробел.
Используйте fread
и fwrite
вместо fscanf
и fprintf
, и ваши каналы будут работать.
Комментарии:
1. @ThomasPadron-McCarthy, которого нет, если закомментированный код добавлен обратно. Прямо сейчас процесс завершается, закрывая поток; если закомментированный код раскомментирован, оба процесса переходят в
fscanf
и становятся взаимоблокированными, ожидая, пока один другой отправит символ пробела.
Ответ №3:
Как в родительском, так и в дочернем процессах, когда вы закончите запись в канал, очистите буферы с помощью fflush
и закройте каналы, запись в которые заканчивается с помощью close
.
Вот немного измененная программа:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
FILE *fdopen(int fd, const char *mode);
int main() {
int child_to_parent[2];
int parent_to_child[2];
pipe(child_to_parent);
pipe(parent_to_child);
pid_t id = fork();
if (id == 0) {
close(parent_to_child[1]);
close(child_to_parent[0]);
FILE* out = fdopen(child_to_parent[1], "w");
FILE* in = fdopen(parent_to_child[0], "r");
char msg[6];
printf("Child gonna read...n");
fscanf(in ,"%s", msg);
printf("Child got: %sn", msg);
fprintf(out, "hi");
fflush(out);
printf("Child sent: hin");
close(child_to_parent[1]);
} else {
close(parent_to_child[0]);
close(child_to_parent[1]);
FILE* in = fdopen(child_to_parent[0], "r");
FILE* out = fdopen(parent_to_child[1], "w");
fprintf(out, "hello");
printf("Parent sent: hellon");
fflush(out);
close(parent_to_child[1]);
char msg[3];
printf("Parent gonna read...n");
fscanf(in, "%s", msg);
printf("Parent got: %sn", msg);
}
}
Вывод при его запуске:
Parent sent: hello
Parent gonna read...
Child gonna read...
Child got: hello
Child sent: hi
Parent got: hi