Potoki - Bad file descriptor

Odpowiedz Nowy wątek
2018-04-16 18:17
0

Witam. Piszę program mający być rozwiązaniem problemu producenta i konsumenta z użyciem potoków nienazwanych. Niestety, na swojej drodze natrafiłem na błąd, którego nie jestem w stanie rozwiązać. Program kompiluje się, ale uruchomienie skutkuje wyświetleniem dla obu procesów błędu "Bad file descriptor". Próby doprowadziły mnie do wniosku, że problem tkwi w potoku. Szukałem rozwiązania w internecie, porównywałem sposoby użycia funkcji read() oraz write(), lecz wciąż nie wiem, co dokładnie jest nie tak. Proszę o pomoc, co robię źle, gdzie popełniłem błąd?

Poniżej kod programu:
prod.c

#include "lib.h"

int main()
{
    int desk1=open("./plik1", O_RDONLY, 0644);
    int buf[50];
    if (desk1==-1)
    {
        printf("Nie da rady...\n");
        exit(3);
    }
    //deskryptor pliku producenta

    const int child_pid=fork();
    //utworzenie procesu potomnego

    if(pipe(fd)==-1)
    {
        perror("Blad funkcji pipe()\n");
        exit(1);
    }

    switch(child_pid)
    {
        case -1:
          perror("Blad funkcji fork(). Cos poszlo nie tak...\n");
          exit(2);
          break;
        case 0:
        //sleep(1);
          execl("./kons.x", "kons.x", NULL);
          perror("Blad fukcji exec().\n");
        default:
            close(fd[1]);

        while(read(desk1, buf, 50))
        {
          if(write(fd[0], buf, PIPE_BUF)<0)
            {
                perror("Nie odczytam...\n");
                exit(4);
            }
        }
        close(fd[0]);
            wait(NULL);
          break;
    }
return 0;
}

kons.c:

#include "lib.h"

int main()
{
    char buf[50];
    int desk2=open("./plik2", O_WRONLY, 0644);
    if(desk2==-1)
        {
            perror("Nie moge otworzyc plik2...\n");
            exit(6);
        }
    //sleep(1);
    close(fd[0]);
    while(read(fd[1], buf, 50))
    {
    if(read(buf, &desk2, 50)==-1)
        {
            perror("Nie moge zapisac...\n");
            exit(5);
        }
    }
    close(fd[1]);
return 0;
}

lib.h:

#ifndef LIB_H
#define LIB_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <limits.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>

    int fd[2];

#endif

Pozostało 580 znaków

2018-04-16 19:20
1
  1. Nie sprawdzasz, czy open(2) zwraca poprawny deskryptor pliku, a nie na przykład błąd.
  2. To samo w przypadku fork(2), który też może zwrócić błąd.
  3. int fd[2]; w pliku .h to słabe rozwiązanie.
  4. De facto nie używasz potoków nienazwanych. man 2 pipe.
  5. Jak zwykle komplikujesz niepotrzebnie kod.
  6. Otwierasz plik, zapisujesz jego fd do jednej zmiennej, po czym natychmiast zamykasz inną...
  7. fd[2] jest w przypadku drugiego programu niezainicjalizowane. exec(2) zamknęło wszystkie FD poza 0,1,2.
edytowany 2x, ostatnio: kapojot, 2018-04-16 19:24

Pozostało 580 znaków

2018-04-16 19:51
0

@kapojot:

  1. Rozumiem, że warunek if(desk1==-1) jest niepoprawny?
  2. Podobnie, jak case -1?
  3. Niestety, był to jedyny sposób, by kompilator nie zwracał błędu
  4. Zajrzałem do man, jednak nie widzę różnicy pomiędzy podanym tam przykładem, a moim użyciem funkcji pipe(). Mógłbyś wyjaśnić, co na tym etapie robię źle?

Pozostało 580 znaków

2018-04-16 20:05
1

Hmm, odnośnie 1 i 2 - przeoczyłem, za dużo kodu na dziś.
Odnośnie 3 - przerobię ten kod "po swojemu", to znowu podyskutujemy, jak to można inaczej rozwiązać.
Odnośnie 4 - tak, wołasz pipe po a nie przed forkiem :)

Pozostało 580 znaków

2018-04-16 20:46
1

Zerknij na poniższy kod.

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
parent(int fd_to_child)
{
    int desk1;
    char buf[50];
    ssize_t bytes_read, bytes_written;

    desk1 = open("./plik1", O_RDONLY);
    if (desk1 < 0)
        err(2, "open(2) failed.");

    do {
        bytes_read = read(desk1, buf, sizeof(buf));
        if (bytes_read < 0)
            err(7, "read(2) failed.");

        bytes_written = 0;
        while (bytes_written < bytes_read) {
            ssize_t ret;

            ret = write(fd_to_child, buf + bytes_written,
                bytes_read - bytes_written);
            if (ret < 0)
                err(4, "write(2) failed.");

            bytes_written += ret;
        }
    } while (bytes_read > 0);

    close(desk1);
    close(fd_to_child);
    wait(NULL);

    return (0);
}

int
child(int fd_from_parent)
{
    int desk2;
    char buf[50];
    ssize_t bytes_read, bytes_written;

    desk2 = open("./plik2", O_WRONLY | O_CREAT, 0644);
    if (desk2 == -1)
        err(6, "open(2) failed.");

    do {
        bytes_read = read(fd_from_parent, buf, sizeof(buf));
        if (bytes_read < 0)
            err(5, "read(2) failed.");

        bytes_written = 0;
        while (bytes_written < bytes_read) {
            ssize_t ret;

            ret = write(desk2, buf + bytes_written, bytes_read -
                bytes_written);
            if (ret < 0)
                err(5, "write(2) failed.");

            bytes_written += ret;
        }

    } while (bytes_read > 0);

    close(desk2);
    close(fd_from_parent);

    return (0);
}

int
main()
{
    int fd[2];
    pid_t child_pid;

    if (pipe(fd) == -1) {
        perror("Blad funkcji pipe()\n");
        exit(1);
    }

    child_pid = fork();
    if (child_pid == -1)
        err(2, "fork(2) failed");

    if (child_pid == 0) {
        close(fd[0]);
        return (child(fd[1]));
    }

    close(fd[1]);
    return (parent(fd[0]));
}

Pozostało 580 znaków

2018-04-17 16:11
0

@kapojot: Dzięki wielkie. Mógłbyś jeszcze wyjaśnić mi, dlaczego funkcja write została zapisana w taki sposób:

            ret = write(fd_to_child, buf + bytes_written,
                bytes_read - bytes_written);

Oraz co oznacza nazwa zmiennej "ret"?

Pozostało 580 znaków

2018-04-17 22:21
1

Zmienną pomocniczą, która służy do sprawdzenia, co zwróciła funkcja write.

A teraz dłuższe wyjaśnienie, czemu tam znalazła się taka pato-litania. write (podobnie zresztą jak read) mogą - uwaga - zwrócić wczytaną liczbę bajtów mniejszą niż rozmiar podanego bufora. Oznacza to, że np. masz bufor 50 bajtów, ale write zdołał zapisać tylko 15 z nich. Co należy zrobić? Ponowić write, podając tym razem przesunięty wskaźnik o liczbę zapisanych uprzednio bajtów oraz ilość bajtów do zapisania pomniejszoną o te już zapisane. Poczytaj dokładnie manual do obu tych funkcji. To dość częsty błąd popełniany przez osoby zaczynające programować w C.

edytowany 2x, ostatnio: kapojot, 2018-04-17 22:28

Pozostało 580 znaków

2018-04-17 23:28
0

Właśnie zauważyłem, że próba wypisania na ekran bezpośrednio z bufora skutkuje otrzymaniem tekstu wraz z śmieciami. Mógłbyś mi podpowiedzieć, jak wypisać zawartość bufora z ich pominięciem?

Pozostało 580 znaków

2018-04-17 23:31
1

Zapewne nie masz terminatora po danych wczytanych z read. Powiększ bufor o 1 znak i po odczycie n bajtów umieść '\0' w miejscu n+1.

Pozostało 580 znaków

2018-04-17 23:48
0

Postanowiłem spróbować zrobić to w poniższy sposób (poniższy fragment kodu wrzuciłem do pliku konsumenta, zaraz za przypisaniem zmiennej ret wartości funkcji write()). Czy taki sposób jest prawidłowy? Dodam, że wypisywane jest dokładnie to, co chcę.

      char text[(bytes_read-bytes_written)+1];
      int i=0;
      for(i=0; i<bytes_read-bytes_written; i++)
        {
          text[i]=buf[i];
        }
      text[bytes_read-bytes_written]='\0';
      printf("%s", text);
edytowany 2x, ostatnio: nojaniewiem, 2018-04-18 11:28

Pozostało 580 znaków

2018-04-18 13:14
1

Przekombinowałeś. Nawet, jeśli działa, to jest zbyt skomplikowane - spróbuj wymyśleć prostszy sposób.

bytes_read - bytes_written aż się prosi o dodatkową zmienną lokalną.

edytowany 1x, ostatnio: kapojot, 2018-04-18 13:14

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0