Производитель-потребитель между двумя детьми раздвоены [C]
У меня проблема с этим известным вопросом. Я должен раскошелиться на двух детей (производитель и потребитель), которые общаются благодаря трубе. Первый дочерний элемент (производитель) должен прочитать строки из стандартного ввода, отправить их второму дочернему элементу (потребителю), который должен преобразовать их в верхний регистр и вывести в стандартный вывод.
Я написал код, но он не работает.
- Потребитель читает строку из stdin и записывает ее в трубу рядом с ней (включая '\0').
- Производитель читает MAXC из трубы и разделяет на две длины и строку. Когда я печатаю, длина устанавливается на большое число. Так что даже строка, которая должна быть преобразована, неверна. Кроме того, вся программа зависает.
(Я пытался написать здесь код, но, возможно, я не понял, как это сделать правильно. Объясните мне! Спасибо)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#define MAXC 30
static void signalHandler(int signo)
{
return;
}
pid_t pids[2];
int main()
{
int fd[2], i, nW;
size_t l;
char string[MAXC+1], stringtoup[MAXC+1], tmp[MAXC+1];
signal(SIGUSR1, signalHandler);
if(pipe(fd)==0)
{
pids[0]=fork();
if(pids[0]==0)
{
fprintf(stdout, "PID=%d PRODUCER\n", getpid());
close(fd[0]); //produttore
sleep(3);
fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
fscanf(stdin, "%s", string);
while(1)
{
if(strcmp(tmp, "end")==0)
break;
l=strlen(string)+1;
sprintf(tmp, "%2lu%s", l, string);
printf("%s\n", tmp);
nW=write(fd[1], tmp, (l+2));
printf("nW(PRODUCER)=%d", nW);
if(nW!=(l+2))
{
perror("wrote not whole string");
exit(1);
}
sleep(5);
kill(pids[1], SIGUSR1);
pause();
fprintf(stdout, "Insert string:\n");
fscanf(stdin, "%s", string);
}
exit(0);
}
pids[1]=fork();
if(pids[1]==0)
{
fprintf(stdout, "PID=%d CONSUMER\n", getpid());
close(fd[0]); //consumer
while(1)
{
pause();
read(fd[0], tmp, MAXC+1);
printf("tmp(CONSUMER)=%s\n", tmp);
sscanf(tmp, "%2lu%s", &l, stringtoup);
printf("lenght string(CONSUMER)=%2lu\n", l);
printf("stringtoup=%s\n", stringtoup);
for(i=0; i<l; i++)
stringtoup[i]=toupper(stringtoup[i]);
fprintf(stdout, "%s\n", stringtoup);
fflush(stdout);
sleep(1);
kill(pids[0], SIGUSR1);
}
exit(0);
}
sleep(4);
for(i=0; i<2; i++)
{
waitpid(pids[i], NULL, 0);
fprintf(stdout, "PID=%d exited\n", pids[i]);
}
}
return(0);
}
редактировать: код исправлен
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#define MAXC 30
pid_t pids[2];
int main()
{
int fd[2], i, nW;
size_t l;
char string[MAXC+1], stringtoup[MAXC+1], tmp[MAXC+1];
if(pipe(fd)==0)
{
pids[0]=fork();
if(pids[0]==0)
{
fprintf(stdout, "PID=%d PRODUCER\n", getpid());
close(fd[0]); //produttore
fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
fscanf(stdin, "%s", string);
while(1)
{
if(strcmp(string, "end")==0)
break;
l=strlen(string)+1;
sprintf(tmp, "%2lu%s", l, string);
nW=write(fd[1], tmp, (l+2));
if(nW!=(l+2))
{
perror("wrote not whole string");
exit(1);
}
sleep(1);
fscanf(stdin, "%s", string);
}
kill(pids[1], SIGINT);
exit(0);
}
pids[1]=fork();
if(pids[1]==0)
{
fprintf(stdout, "PID=%d CONSUMER\n", getpid());
close(fd[1]); //consumer
while(1)
{
read(fd[0], tmp, MAXC+1);
sscanf(tmp, "%2lu%s", &l, stringtoup);
for(i=0; i<l; i++)
stringtoup[i]=toupper(stringtoup[i]);
fprintf(stdout, "%s\n", stringtoup);
fflush(stdout);
sleep(1);
}
exit(0);
}
for(i=0; i<2; i++)
{
waitpid(pids[i], NULL, 0);
fprintf(stdout, "PID=%d exited\n", pids[i]);
}
}
return(0);
}
1 ответ
Читая комментарии, я предполагаю, что вопрос изменился и касается процедуры kill-wait.
Проблема должна заключаться в том, что дети не имеют ничего общего с памятью родителей с момента их разветвления.
Первый ребенок (производитель) имеет pids
массив заполнен как:
pids[0] == 0;
pids[1] == undefined;
Значение pids[1]
никогда не меняется, так как этот процесс никогда не изменяет эту часть своей собственной памяти.
Второй ребенок (потребитель) имеет pids
массив заполнен как:
pids[0] == first_child's pid;
pids[1] == 0;
В общем, только родитель может убить второго ребенка (один с pids[1]
), не первый ребенок. Таким образом, "производитель" убивает процесс с помощью некоторого pid, которого вы не знаете (это может вызвать более широкую системную проблему, если не повезет), поэтому "потребитель" никогда не получит его.
Когда на трубе нет писателей, read()
функция возвращает ноль (0
). Это то, чем вы должны воспользоваться, сломаться, когда это произойдет, и выйти. С другой стороны, когда есть писатели, read()
функциональные блоки, пока есть что почитать, поэтомуsleep(1)
не нужен
Как я вижу, программа завершается ошибкой, когда выполняется выполнение этого случайного процесса, и поэтому родитель не печатает выход любого потомка. Однако, если вы удалите это kill(...)
закройте конец записи канала и проверьте возвращаемое значение read(...)
программа работает как надо.
Кроме того, хорошим примером является закрытие всех ненужных fds (даже если функция выхода сделает это в какой-то момент), поэтому родительский объект сразу после появления всех потомков может закрыть 2 fds (он должен закрыть запись - конец для читателя, чтобы сломаться!), и читатель может закрыть оставшийся FD до выхода. Ниже приведены части кода, которые необходимо исправить.
...
if(pids[0]==0)
{
fprintf(stdout, "PID=%d PRODUCER\n", getpid());
close(fd[0]); //produttore
fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
fscanf(stdin, "%s", string);
while(1)
{
if(strcmp(string, "end")==0)
break;
l=strlen(string)+1;
sprintf(tmp, "%2lu%s", l, string);
nW=write(fd[1], tmp, (l+2));
if(nW!=(l+2))
{
perror("wrote not whole string"); exit(1);
} sleep(1);
fscanf(stdin, "%s", string);
}
close(fd[1]);
exit(0);
}
pids[1]=fork();
if(pids[1]==0)
{
fprintf(stdout, "PID=%d CONSUMER\n", getpid());
close(fd[1]); //consumer
while(1)
{
if (read(fd[0], tmp, MAXC+1) == 0)
break;
sscanf(tmp, "%2lu%s", &l, stringtoup);
for(i=0; i<l; i++)
stringtoup[i]=toupper(stringtoup[i]);
fprintf(stdout, "%s\n", stringtoup);
fflush(stdout);
sleep(1); // this could be removed
}
close(fd[0]);
exit(0);
}
close(fd[0]);
close(fd[1]);
for(i=0; i<2; i++)
...