We wszystkich językach programowania (których przynajmniej używam) musisz otworzyć plik, zanim będziesz mógł go odczytać lub napisać.
Ale co właściwie robi ta otwarta operacja?
Strony podręcznika dla typowych funkcji tak naprawdę nie mówią nic poza tym, że „otwiera plik do odczytu / zapisu”:
http://www.cplusplus.com/reference/cstdio/fopen/
https://docs.python.org/3/library/functions.html#open
Oczywiście poprzez użycie funkcji można powiedzieć, że wiąże się to z utworzeniem pewnego rodzaju obiektu, który ułatwia dostęp do pliku.
Innym sposobem umieszczenia tego byłoby, gdybym zaimplementował open
funkcję, co musiałby zrobić w systemie Linux?
C
i Linux; ponieważ to, czym różnią się Linux i Windows. W przeciwnym razie jest nieco za szeroki. Ponadto każdy język wyższego poziomu wywoła albo C API dla systemu, albo skompiluje do C w celu wykonania, więc pozostawienie na poziomie „C” stawia go na poziomie najmniej wspólnego mianownika.Odpowiedzi:
W prawie każdym języku wysokiego poziomu funkcja otwierająca plik jest otoczona odpowiednim wywołaniem systemowym jądra. Może także robić inne wymyślne rzeczy, ale we współczesnych systemach operacyjnych otwieranie pliku musi zawsze przechodzić przez jądro.
Dlatego argumenty
fopen
funkcji bibliotecznej lub Python sąopen
bardzo podobne do argumentówopen(2)
wywołania systemowego.Oprócz otwierania pliku funkcje te zwykle ustawiają bufor, który będzie konsekwentnie używany z operacjami odczytu / zapisu. Celem tego bufora jest zapewnienie, że ilekroć chcesz odczytać N bajtów, odpowiednie wywołanie biblioteki zwróci N bajtów, niezależnie od tego, czy wywołania bazowych wywołań systemowych zwracają mniej.
W systemach operacyjnych typu Unix udane wywołanie
open
zwraca „deskryptor pliku”, który jest jedynie liczbą całkowitą w kontekście procesu użytkownika. W konsekwencji ten deskryptor jest przekazywany do każdego wywołania, które wchodzi w interakcję z otwartym plikiem, a po wywołaniuclose
deskryptor traci ważność.Należy zauważyć, że wezwanie do
open
działania działa jak punkt sprawdzania poprawności, w którym przeprowadzane są różne kontrole. Jeśli nie wszystkie warunki są spełnione, wywołanie kończy się niepowodzeniem przez powrót-1
zamiast deskryptora, a rodzaj błędu jest wskazany werrno
. Niezbędne kontrole to:W kontekście jądra musi istnieć pewnego rodzaju mapowanie między deskryptorami plików procesu a fizycznie otwieranymi plikami. Wewnętrzna struktura danych odwzorowana na deskryptor może zawierać jeszcze inny bufor, który obsługuje urządzenia oparte na blokach, lub wewnętrzny wskaźnik wskazujący bieżącą pozycję odczytu / zapisu.
źródło
man dup2
i sprawdź subtelność między otwartym deskryptorem pliku (to jest FD, który jest otwarty) a otwartym opisem pliku (OFD).Proponuję rzucić okiem na ten przewodnik po uproszczonej wersji
open()
wywołania systemowego . Używa następującego fragmentu kodu, który reprezentuje to, co dzieje się za scenami po otwarciu pliku.W skrócie, oto, co robi ten kod, wiersz po wierszu:
filp_open
Funkcja ma realizacjęktóra robi dwie rzeczy:
struct file
z podstawowymi informacjami na temat i-węzła i zwróć go. Ta struktura staje się wpisem na liście otwartych plików, o której wspomniałem wcześniej.Przechowuj („instaluj”) zwróconą strukturę na liście otwartych plików procesu.
read()
,write()
iclose()
. Każdy z nich przekaże kontrolę jądra, które może użyć deskryptora pliku, aby wyszukać odpowiedni wskaźnik pliku na liście procesu i użyć informacji w tym wskaźniku pliku, aby faktycznie wykonać odczyt, zapis lub zamknięcie.Jeśli masz ambicję, możesz porównać ten uproszczony przykład do implementacji
open()
wywołania systemowego w jądrze Linuksa, funkcji o nazwiedo_sys_open()
. Nie powinieneś mieć problemów ze znalezieniem podobieństw.Oczywiście jest to tylko „górna warstwa” tego, co dzieje się podczas wywoływania
open()
- a ściślej, jest to najwyższy poziom kodu jądra, który jest wywoływany podczas otwierania pliku. Język programowania wysokiego poziomu może dodać do tego dodatkowe warstwy. Na niższych poziomach dzieje się wiele. (Podziękowania dla Ruslana i pjc50 za wyjaśnienie.) Z grubsza, od góry do dołu:open_namei()
identry_open()
wywołać kod systemu plików, który jest również częścią jądra, aby uzyskać dostęp do metadanych i zawartości plików i katalogów. System plików odczytuje surowe bajty z dysku i interpretuje te wzorce bajtów jako drzewo plików i katalogów./dev/sda
itp.)Może to być również nieco niepoprawne z powodu buforowania . :-P Poważnie, jednak pominęłem wiele szczegółów - osoba (nie ja) mogłaby napisać wiele książek opisujących, jak działa cały ten proces. Ale to powinno dać ci pomysł.
źródło
Każdy system plików lub system operacyjny, o którym chcesz rozmawiać, jest dla mnie w porządku. Miły!
W przypadku ZX Spectrum zainicjowanie
LOAD
polecenia wprowadzi system w ścisłą pętlę, odczytując linię Audio In.Początek danych jest sygnalizowany stałym tonem, a następnie następuje sekwencja długich / krótkich impulsów, przy czym krótki impuls jest dla binarnego,
0
a dłuższy dla binarnego1
( https://en.wikipedia.org/ wiki / ZX_Spectrum_software ). Ciasna pętla obciążenia zbiera bity, aż wypełni bajt (8 bitów), zapisuje to w pamięci, zwiększa wskaźnik pamięci, a następnie zapętla z powrotem, aby wyszukać więcej bitów.Zazwyczaj pierwszą rzeczą, którą czytnik powinien przeczytać, jest krótki nagłówek o stałym formacie , wskazujący co najmniej oczekiwaną liczbę bajtów i ewentualnie dodatkowe informacje, takie jak nazwa pliku, typ pliku i adres ładowania. Po przeczytaniu tego krótkiego nagłówka program może zdecydować, czy kontynuować ładowanie głównej części danych, czy zakończyć procedurę ładowania i wyświetlić odpowiedni komunikat dla użytkownika.
Stan końca pliku można rozpoznać po otrzymaniu tylu bajtów, ile się spodziewa (stałej liczby bajtów zapisanych w oprogramowaniu lub zmiennej liczby, takiej jak wskazana w nagłówku). Zgłoszono błąd, jeśli pętla obciążeniowa nie otrzymywała impulsu w oczekiwanym zakresie częstotliwości przez określony czas.
Małe tło do tej odpowiedzi
Opisana procedura ładuje dane ze zwykłej taśmy audio - stąd potrzeba przeskanowania wejścia audio (jest to połączone ze standardową wtyczką do magnetofonu).
LOAD
Komenda jest technicznie taka sama jakopen
pliku - ale to fizycznie przywiązany do rzeczywiście ładuje plik. Wynika to z faktu, że magnetofon nie jest kontrolowany przez komputer i nie można (pomyślnie) otworzyć pliku, ale nie można go załadować.Wspomniano o „ciasnej pętli”, ponieważ (1) procesor, Z80-A (jeśli pamięć służy), był naprawdę wolny: 3,5 MHz i (2) spektrum nie miało wewnętrznego zegara! Oznacza to, że musiał dokładnie liczyć stany T (czasy instrukcji) dla każdego. pojedynczy. instrukcja. wewnątrz tej pętli, aby zachować dokładny czas sygnału dźwiękowego.
Na szczęście ta niska prędkość procesora miała tę wyraźną zaletę, że można było obliczyć liczbę cykli na kawałku papieru, a tym samym czas rzeczywisty, jaki by to zajęło.
źródło
To zależy od systemu operacyjnego, co dokładnie dzieje się po otwarciu pliku. Poniżej opisuję, co dzieje się w Linuksie, ponieważ daje wyobrażenie o tym, co dzieje się po otwarciu pliku. Możesz sprawdzić kod źródłowy, jeśli jesteś zainteresowany bardziej szczegółowymi informacjami. Nie obejmuję uprawnień, ponieważ spowodowałoby to zbyt długą odpowiedź.
W systemie Linux każdy plik jest rozpoznawany przez strukturę o nazwie i- węzeł. Każda struktura ma unikalny numer, a każdy plik otrzymuje tylko jeden numer i-węzła. Ta struktura przechowuje metadane pliku, na przykład rozmiar pliku, uprawnienia do pliku, znaczniki czasu i wskaźnik do bloków dysku, ale nie samą nazwę pliku. Każdy plik (i katalog) zawiera wpis nazwy pliku i numer i-węzła do wyszukiwania. Po otwarciu pliku, przy założeniu, że masz odpowiednie uprawnienia, deskryptor pliku jest tworzony przy użyciu unikalnego numeru i-węzła związanego z nazwą pliku. Ponieważ wiele procesów / aplikacji może wskazywać na ten sam plik, i-węzeł ma pole łącza, które utrzymuje łączną liczbę łączy do pliku. Jeśli plik jest obecny w katalogu, jego liczba linków wynosi jeden, jeśli ma link twardy, jego liczba linków będzie wynosić dwa, a jeśli plik zostanie otwarty przez proces, liczba linków zostanie zwiększona o 1.
źródło
Głównie księgowość. Obejmuje to różne kontrole, takie jak „Czy plik istnieje?” i „Czy mam uprawnienia do otwierania tego pliku do zapisu?”.
Ale to wszystko jądro - chyba że wdrażasz własny zabawkowy system operacyjny, nie ma w czym zagłębiać się (jeśli tak, baw się dobrze - to wspaniałe doświadczenie edukacyjne). Oczywiście nadal powinieneś nauczyć się wszystkich możliwych kodów błędów, które możesz otrzymać podczas otwierania pliku, abyś mógł sobie z nimi właściwie poradzić - ale są to zwykle ładne małe abstrakcje.
Najważniejszą częścią na poziomie kodu jest to, że daje on uchwyt do otwartego pliku, którego używasz do wszystkich innych operacji wykonywanych z plikiem. Nie możesz użyć nazwy pliku zamiast tego dowolnego uchwytu? Cóż, jasne - ale użycie uchwytu daje pewne korzyści:
read
od ostatniej pozycji w twoim pliku. Używając uchwytu do identyfikacji konkretnego „otwarcia” pliku, możesz mieć wiele współbieżnych uchwytów do tego samego pliku, każdy odczytując z ich własnych miejsc. W pewien sposób uchwyt działa jako ruchome okno do pliku (i sposób wydawania asynchronicznych żądań We / Wy, które są bardzo przydatne).Istnieją również inne sztuczki, które możesz zrobić (na przykład współużytkować uchwyty między procesami, aby mieć kanał komunikacyjny bez użycia pliku fizycznego; w systemach uniksowych pliki są również używane dla urządzeń i różnych innych kanałów wirtualnych, więc nie jest to absolutnie konieczne ), ale tak naprawdę nie są one związane z
open
samą operacją, więc nie zamierzam się w to zagłębiać.źródło
U jej podstaw podczas otwierania się do czytania nic szczególnego nie musi się wydarzyć. Wystarczy sprawdzić, czy plik istnieje, a aplikacja ma wystarczające uprawnienia, aby go odczytać i utworzyć uchwyt, w którym można wydawać polecenia odczytu do pliku.
To na te polecenia zostanie wysłany faktyczny odczyt.
System operacyjny często uzyskuje przewagę w czytaniu, rozpoczynając operację odczytu w celu wypełnienia bufora związanego z uchwytem. Następnie, kiedy faktycznie czytasz, może natychmiast zwrócić zawartość bufora, zamiast czekać na IO dysku.
Aby otworzyć nowy plik do zapisu, system operacyjny będzie musiał dodać pozycję w katalogu dla nowego (obecnie pustego) pliku. I znowu tworzony jest uchwyt, na którym możesz wydawać polecenia zapisu.
źródło
Zasadniczo wywołanie open musi znaleźć plik, a następnie nagrać wszystko, czego potrzebuje, aby później operacje we / wy mogły go ponownie znaleźć. To dość niejasne, ale będzie to prawdą we wszystkich systemach operacyjnych, o których mogę natychmiast pomyśleć. Szczegóły różnią się w zależności od platformy. Wiele odpowiedzi już tutaj mówi o współczesnych komputerowych systemach operacyjnych. Zrobiłem małe programowanie na CP / M, więc zaoferuję swoją wiedzę na temat tego, jak to działa na CP / M (MS-DOS prawdopodobnie działa w ten sam sposób, ale ze względów bezpieczeństwa zwykle nie robi się tak dzisiaj ).
Na CP / M masz coś o nazwie FCB (jak wspomniałeś C, możesz to nazwać struct; to naprawdę jest 35-bajtowy ciągły obszar w pamięci RAM zawierający różne pola). FCB ma pola do zapisania nazwy pliku i (4-bitową) liczbę całkowitą identyfikującą napęd dyskowy. Następnie, wywołując Open File jądra, przekazujesz wskaźnik do tej struktury, umieszczając go w jednym z rejestrów procesora. Jakiś czas później system operacyjny powraca z nieznacznie zmienioną strukturą. Cokolwiek zrobisz we / wy dla tego pliku, przekażesz wskaźnik do tej struktury do wywołania systemowego.
Co CP / M robi z tym FCB? Zastrzega sobie pewne pola na własny użytek i wykorzystuje je do śledzenia pliku, więc lepiej nigdy nie dotykaj ich w programie. Operacja Otwórz plik przeszukuje tabelę na początku dysku w poszukiwaniu pliku o tej samej nazwie, co zawartość FCB (znak wieloznaczny „?” Pasuje do dowolnego znaku). Jeśli znajdzie plik, kopiuje pewne informacje do FCB, w tym fizyczną lokalizację pliku na dysku, tak aby kolejne wywołania We / Wy ostatecznie wywoływały system BIOS, który może przekazać te lokalizacje do sterownika dysku. Na tym poziomie szczegóły są różne.
źródło
Mówiąc prościej, po otwarciu pliku w rzeczywistości żądasz od systemu operacyjnego załadowania żądanego pliku (skopiuj zawartość pliku) z pamięci dodatkowej do pamięci RAM w celu przetworzenia. Powodem tego jest (ładowanie pliku), ponieważ nie można przetworzyć pliku bezpośrednio z dysku twardego ze względu na jego bardzo małą prędkość w porównaniu do Ram.
Polecenie open wygeneruje wywołanie systemowe, które z kolei kopiuje zawartość pliku z pamięci dodatkowej (dysku twardego) do pamięci podstawowej (pamięci RAM).
I „Zamykamy” plik, ponieważ zmodyfikowana zawartość pliku musi zostać odzwierciedlona w oryginalnym pliku znajdującym się na dysku twardym. :)
Mam nadzieję, że to pomaga.
źródło