Zrozumienie współbieżnych zapisów plików z wielu procesów

Stąd: to plik dołączany w Unixie

Rozważ przypadek, w którym wiele procesów otwiera ten sam plik i dołącza do niego. O_append gwarantuje, że wyszukiwanie do końca pliku, a następnie rozpoczęcie operacji zapisu jest atomowe. Tak więc wiele procesów może dołączyć do tego samego pliku i żaden proces nie nadpisze zapisu innych procesów tak dalece, jak każdy rozmiar zapisu wynosi

Napisałem program testowy, w którym wiele procesów otwiera się i zapisuje do tego samego pliku (write(2)). Upewniam się, że każdy rozmiar zapisu to > PIPE_BUF (4k). Spodziewałem się zobaczyć przypadki, w których proces nadpisuje czyjeś dane. Ale tak się nie dzieje. Testowałem z różnymi rozmiarami zapisu. Czy to tylko szczęście, czy jest jakiś powód, dlaczego tak się nie dzieje? Moim ostatecznym celem jest zrozumienie, czy wiele procesów dołączających do tego samego pliku musi koordynować swoje zapisy.

Oto kompletny program. Każdy proces tworzy bufor int, wypełnia wszystkie wartości swoim rank, otwiera / align = "left" /

Specs: OpenMPI 1.4.3 on Opensuse 11.3 64-bit

Skompilowany jako: test mpicc-O3.c, Uruchom jako: mpirun-np 8 ./ a. out

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

int 
main(int argc, char** argv) {
    int rank, size, i, bufsize = 134217728, fd, status = 0, bytes_written, tmp_bytes_written;
    int* buf;
    char* filename = "/tmp/testfile.out";

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    buf = (int*) malloc (bufsize * sizeof(int));   
    if(buf == NULL) {
        status = -1;
        perror("Could not malloc");
        goto finalize;
    }
    for(i=0; i<bufsize; i++) 
        buf[i] = rank;

    if(-1 == (fd = open(filename, O_APPEND|O_WRONLY, S_IWUSR))) {
        perror("Cant open file");
        status = -1;
        goto end;
        exit(-1);
    }

    bytes_written = 0;
    if(bufsize != (tmp_bytes_written = write(fd, buf, bufsize))) {
        perror("Error during write");
        printf("ret value: %d\n", tmp_bytes_written);
        status = -1;
        goto close;
    }

close:
    if(-1 == close(fd)) {
        perror("Error during close");
        status = -1;
    }
end:
    free(buf);
finalize:
    MPI_Finalize();
    return status;
}
Author: Community, 2012-10-18

3 answers

Atomiczność zapisu mniej niż PIPE_BUF dotyczy tylko pipe ' ów i FIFO. W przypadku zapisu pliku POSIX mówi:

Ten wolumin POSIX.1-2008 nie określa zachowania współbieżnych zapisuje do pliku z wielu procesów. Aplikacje powinny korzystać z niektórych forma kontroli współbieżności.

...co oznacza, że jesteś zdany na siebie-różne UNIKSY dają różne gwarancje.

 14
Author: caf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 21:10:27

Po pierwsze, O_APPEND lub równoważny FILE_APPEND_DATA w systemie Windows oznacza, że przyrosty maksymalnego rozmiaru pliku ("długość pliku") są atomowe w współbieżnych pismach, a to jest dowolna ilość, a nie tylko PIPE_BUF. Jest to gwarantowane przez POSIX, a Linux, FreeBSD, OS X i Windows Wszystkie implementują to poprawnie. Samba również implementuje go poprawnie, NFS przed v5 nie, ponieważ nie ma możliwości formatu drutu do dołączania atomicznie. Więc jeśli otworzysz plik za pomocą append-only, współbieżne zapisy nie ulegną rozerwaniu w stosunku do siebie na żadnym większym systemie operacyjnym , chyba że w grę wchodzi NFS.

To nie mówi nic o tym, czy reads kiedykolwiek zobaczy rozdarty zapis, a na tym POSIX mówi co następuje o atomiczności read () I write () do zwykłych plików:

Wszystkie poniższe funkcje są atomowe w odniesieniu do każdej inne w efektach określonych w POSIX.1-2008 kiedy działają na zwykłe pliki lub dowiązania symboliczne ... [wiele funkcji] ... read ()... write ()... Jeśli dwa wątki wywołują jedną z tych funkcji, każde wywołanie musi albo zobaczyć wszystkie określone efekty drugiego wywołania, albo żaden z nich. [źródło]

I

Zapisy mogą być serializowane w odniesieniu do innych odczytów i zapisów. Jeśli a read() danych z pliku może być udowodnione (w dowolny sposób), że wystąpi po write () danych, musi odzwierciedlać to write (), nawet jeśli wywołania są wytwarzane przez różne procesy. [źródło]

Ale odwrotnie:

Ten wolumin POSIX.1-2008 nie określa zachowania współbieżnych zapisuje do pliku z wielu procesów. Aplikacje powinny korzystać z niektórych forma kontroli współbieżności. [źródło]

Bezpieczna interpretacja wszystkich trzech z tych wymagań sugerowałaby, że wszystkie zapisy nakładające się w tym samym pliku muszą być serializowane względem siebie i tak odczytywane, aby torn nie zapisywał nigdy ukazać się czytelnikom.

Mniej bezpieczna, ale nadal dozwolona interpretacja może być taka, że odczyt i zapis tylko serializują się ze sobą między wątkami wewnątrz tego samego procesu, a między procesami zapisy są serializowane tylko w odniesieniu do odczytu (tzn. istnieje sekwencyjnie spójna kolejność we/wy między wątkami w procesie, ale między procesami we/wy jest tylko acquire-release).

Oczywiście to, że standard wymaga takiej semantyki nie oznacza, że implementacje są zgodne, chociaż w rzeczywistości FreeBSD z ZFS zachowuje się doskonale, bardzo najnowsze okna (10.0.14393) z NTFS zachowuje się doskonale, a ostatnie Linuksy z ext4 zachowują się poprawnie, jeśli O_DIRECT jest włączony. jeśli chcesz uzyskać więcej szczegółów na temat tego, jak główne systemy operacyjne i systemy archiwizacji są zgodne ze standardem, zapoznaj się z tą odpowiedzią

 11
Author: Niall Douglas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-21 18:22:01

To nie jest szczęście, w tym sensie, że jeśli zagłębisz się w jądro, możesz prawdopodobnie udowodnić, że w Twoich szczególnych okolicznościach nigdy nie zdarzy się, że jeden proces' write jest przeplatany z innym. Zakładam, że:

  • nie przekraczasz żadnych limitów rozmiaru pliku
  • nie wypełniasz systemu plików, w którym tworzysz plik testowy
  • plik jest zwykłym plikiem (Nie gniazdem, rurą lub czymś innym)
  • system plików jest lokalny
  • The bufor nie obejmuje wielu mapowań pamięci wirtualnej (ten jest znany jako prawdziwy, ponieważ jest to malloc()ed, który umieszcza go na stercie, która przylega do niego.
  • procesy nie są przerywane, sygnalizowane ani śledzone, gdy write() jest zajęty.
  • nie występują błędy We/Wy dysku, awarie pamięci RAM ani inne nietypowe warunki.
  • (być może inni)

Prawdopodobnie rzeczywiście okaże się, że jeśli wszystkie te założenia trzymać, jest to przypadek, że jądro systemu operacyjnego można zdarza się, że użycie zawsze wykonuje pojedyncze wywołanie systemowe write() z pojedynczym atomowym zapisem do następującego pliku.

To nie znaczy, że możesz liczyć na to, że to zawsze będzie prawda. Nigdy nie wiadomo kiedy to może nie być prawda kiedy:
  • program jest uruchamiany na innym systemie operacyjnym
  • plik przenosi się do systemu plików NFS
  • proces otrzymuje sygnał, gdy write() jest w toku, a write() zwraca częściowy wynik (mniej bajtów niż wymagane). Nie jestem pewien, czy POSIX naprawdę na to pozwala, ale programuję defensywnie!
  • itd...

Więc Twój eksperyment nie może udowodnić, że możesz na non-interleaved pisze.

 5
Author: Celada,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 21:11:10