Количество записанных байтов всегда равно -1

Я пытаюсь реализовать некоторые IPC без мьютексов, условий и семафоров, используя только каналы. Следующий код представляет родительский процесс, разветвляющийся на 3 дочерних процесса (p11,p12,p13). Родитель пробуждает всех дочерних процессов сигналом SIGUSR2, затем p11 начинает записывать 50 случайных чисел в конвейер. p12 и p13 конкурентно читают из конвейера и записывают полученные числа в свой собственный файл (file12 и file13). 50-е число - это -1, как только дети читают -1, они выходят. write, используемый в дочернем элементе p11, просто не записывает ни одного байта, он возвращает -1. Я могу использовать только вызовы write(2) и read(2) для записи и чтения. У меня не получается это реализовать, пробовал несколько раз, безрезультатно. Надеюсь, вы можете мне помочь.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
void handler(int signo)
{
    printf("\nSignal intercepted!\n");
}

void main ()
{
srand(time(NULL));
signal(SIGUSR2,handler);
int pa[2];
pipe(pa[2]); //create pipe
pid_t pid1,pid2,pid3;
if((pid1=fork())==0)  //child 11
    {
        close(pa[0]); //close the read side of the pipe
        pause();
        int i,nwrite,num;
        for(i=0;i<49;i++)
            {

            num=rand()%100+1; //generate a random number
            nwrite=write(pa[1],&num,sizeof(int)); //write it to the  pipe
            if(nwrite==-1) //if there's a write error
                {
                printf("\nWrite error..\n");
                exit(-1);
                }
            }
        num=-1; //generate the last number
        nwrite=(write(pa[1],&num,sizeof(int))); //write the last number to the pipe
        if (nwrite==-1) //if there's a write error
            {
            printf("\nError,now exiting...\n");
            exit(-1);
            }   
        close(pa[1]); //close the pipe in write mode
        exit(1); 
    }

else if ((pid2=fork())==0) //child 12
    {

    close(pa[1]); //close the write side of the pipe
    pause();
    int fd1,nread,num;
    fd1=open("file12.txt",O_CREAT,0777); //create a new file
    while((nread=read(pa[0],&num,sizeof(num)))>0)  //while there are bytes to read
        {
            if(num!=-1) //if this isn't the last number
                {
            write(fd1,&num,sizeof(num)); //write it to the file
                }
            else
                {   
                printf("\n-1 sent!\n"); //notify the last read number
                close(pa[0]); //close the read side of the pipe
                    close(fd1); //close the file descriptor
                    exit(1); //exit
                    }
            }   
        }



    else if ((pid3=fork())==0) //child 13, same as 12
        {
            close(pa[1]);   
            pause();
            int fd2,nread,num;
            fd2=open("file13.txt",O_CREAT,0777);

            while((nread=read(pa[0],&num,sizeof(num)))>0)
                {
                    if(num!=-1)
                    {
                            write(fd2,&num,sizeof(num));
                    }
                    else
                    {
                        printf("\n-1 sent!\n");
                        close(pa[0]);
                        close(fd2);
                        exit(1);
                    }
                }   
        }




else //parent
    {   
        sleep(1);
        kill(pid1,SIGUSR2); //wake up all the childs
        kill(pid2,SIGUSR2);
        kill(pid3,SIGUSR2);
        waitpid(pid1,0,NULL); //wait for the childs to end
        waitpid(pid2,0,NULL);
        waitpid(pid3,0,NULL);
        printf("\nDone, now exiting...\n"); //exit
    }

}

person Giulio Paoli    schedule 01.07.2016    source источник
comment
Разве компилятор не кричит от боли на это: pipe(pa[2]);?   -  person alk    schedule 01.07.2016
comment
Также, чтобы получить более подробную информацию в случае сбоя функций, замените эти вещи, как это printf("\nWrite error..\n"); на perror("write() failed");   -  person alk    schedule 01.07.2016
comment
Также^2 : Вы не должны печатать из обработчиков сигналов.   -  person alk    schedule 01.07.2016
comment
Также^3: добавьте проверку ошибок для всех соответствующих вызовов функций. релевантными следует считать те, которые могут дать сбой и тем самым повлиять на логический ход программы. Это не только не сделает вашу программу безопаснее, но и отладит ее бесплатно.   -  person alk    schedule 01.07.2016
comment
Спасибо всем за быстрые ответы. Я уже проверил некоторые из этих соответствующих функций, поэтому я не проверял их в этом фрагменте кода. Но да, ты прав, я должен это сделать. Почему я не должен печатать в обработчиках сигналов? Чтобы избежать ошибок буфера?   -  person Giulio Paoli    schedule 01.07.2016
comment
В обработчике сигнала следует использовать только те функции, которые гарантированно безопасны для асинхронного сигнала. printf() нет. Читайте здесь pubs.opengroup.org/onlinepubs/9699919799/functions/, чтобы найти список функций, которые можно использовать из обработчиков сигналов.   -  person alk    schedule 01.07.2016
comment
Спасибо за ценную информацию. Всегда хорошо узнавать новое.   -  person Giulio Paoli    schedule 01.07.2016
comment
когда какой-либо параметр функции не используется (например, параметр signo), то первая строка в теле функции должна быть: (void)signo; При компиляции всегда включайте все предупреждения, а затем исправьте эти предупреждения. Среди МНОГИХ других проблем есть только две действительные подписи для main() 1) int main( void ) 2) int main( int, char ** ) IE. возвращаемый тип из main() всегда int   -  person user3629249    schedule 02.07.2016
comment
для простоты чтения и понимания 1) последовательно отступайте код, отступ после каждой открывающей скобки '{'. уберите отступ перед каждой закрывающей фигурной скобкой '}', никогда не используйте табуляцию для отступа. 2) используйте одинаковые интервалы по горизонтали и вертикали. Для вертикального интервала: каждый блок кода (for, if, else, while, do...while, switch, case, default) должен быть разделен пустой строкой (а не каким-то случайным числом (включая 0) пустых строк   -  person user3629249    schedule 02.07.2016
comment
функция: fork() имеет три вида возвращаемых значений: ‹0 при возникновении ошибки, ==0 при выполнении дочернего процесса, ›0 при выполнении родительского процесса. Опубликованный код не обрабатывает состояние ошибки   -  person user3629249    schedule 02.07.2016
comment
функция time() возвращает time_t, однако функция srand() ожидает параметр unsigned int, чтобы избежать предупреждений компилятора, преобразуйте возвращаемое значение из time() в unsigned int.   -  person user3629249    schedule 02.07.2016
comment
при компиляции всегда включайте все предупреждения, а затем исправьте эти предупреждения. (для gcc при минимальном использовании: -Wall -Wextra -pedantic также использую: -Wconversion -std-gnu99 )   -  person user3629249    schedule 02.07.2016
comment
функция: write() возвращает ssize_t, а не int. Функция read() возвращает ssize_t, а не int   -  person user3629249    schedule 02.07.2016
comment
Пожалуйста, прочитайте справочную страницу для системных функций, которые использует код. Третий параметр waitpid() — это и int, а не указатель, поэтому третий параметр должен быть равен 0, а не NULL.   -  person user3629249    schedule 02.07.2016
comment
относительно этой (и подобных строк) nwrite=(write(pa[1],&num,sizeof(int))); дополнительный набор скобок просто загромождает код, делая его намного более трудным для понимания, отладки и обслуживания. Предложите удалить внешний набор скобок.   -  person user3629249    schedule 02.07.2016
comment
при вызове системных функций (например, open()) всегда проверяйте возвращаемое значение, чтобы убедиться, что функция выполнена успешно, прежде чем использовать результаты вызова этой функции.   -  person user3629249    schedule 02.07.2016
comment
Спасибо. Я ценю ваши усилия.   -  person Giulio Paoli    schedule 04.07.2016


Ответы (1)


По крайней мере, это*1

int pa[2];
pipe(pa[2]);

должно быть

int pa[2];
pipe(pa);

Фон:

pa[2] оценивается как int. pipe(), с другой стороны, ожидает int[2], который в контексте определения параметра функции совпадает с int[], который совпадает с int *.

Если массив передается функции, он распадается на указатель на его 1-й элемент. Таким образом, передача pa в pipe() приведет к передаче &pa[0], что на самом деле является int *.


*1 Кстати, кодирование pa[2] в любом случае вызывает неопределенное поведение, поскольку оно считывает 3-й элемент pa, а pa содержит только два элемента.

person alk    schedule 01.07.2016
comment
Большое спасибо. Это была ошибка отвлечения внимания, черт возьми. Я сходил с ума по этому поводу. Любая идея о том, почему после исправления строки pipe(pa) дочерние элементы по-прежнему ничего не записывают в файлы? - person Giulio Paoli; 01.07.2016
comment
@GiulioPaoli: Настройте и завершите обработку ошибок и ведение журнала кода, как это было предложено в моих комментариях к вашему вопросу, и получите просвещение ... ;-) - person alk; 01.07.2016
comment
@GiulioPaoli: Также серьезно относитесь к предупреждениям компилятора и исправьте код, пока они не исчезнут. - person alk; 01.07.2016
comment
Ооо, я буду. Спасибо, еще раз. - person Giulio Paoli; 01.07.2016
comment
Эй, просто за то, что я решаю это! благодарю вас. Проверив каждую соответствующую функцию в моем коде, я обнаружил, что запись в файл не удалась из-за плохого открытия файла сбоя. Я звонил open с флагом O_CREAT, но без O_RDWR! Это не открывало файл в режиме чтения-записи и поэтому не работало. Еще раз спасибо. Этот совет сэкономит мне столько времени! - person Giulio Paoli; 01.07.2016
comment
Рад, что это помогло. Также вы можете очень хорошо добавить ответ на свой вопрос. И даже принять это потом. - person alk; 01.07.2016
comment
Что ж, твой был лучшим, значит, твой принят! - person Giulio Paoli; 01.07.2016