Co dzieje się z dojściem do otwartego pliku w systemie Linux, jeśli wskazany plik zostanie przeniesiony lub usunięty
107
Co dzieje się z dojściem do otwartego pliku w systemie Linux, jeśli w międzyczasie wskazany plik otrzyma:
Usunięto -> Czy uchwyt pliku pozostaje ważny?
Usunięte -> Czy prowadzi to do EBADF, wskazującego nieprawidłowy uchwyt pliku?
Zastąpiony przez nowy plik -> Czy plik wskazuje na ten nowy plik?
Zastąpiony przez twardy link do nowego pliku -> Czy mój plik obsługuje ten link?
Zastąpiony przez miękki link do nowego pliku -> Czy mój uchwyt pliku trafia teraz do tego miękkiego linku?
Dlaczego zadaję takie pytania: używam sprzętu podłączanego na gorąco (takiego jak urządzenia USB itp.). Może się zdarzyć, że urządzenie (a także jego / dev / plik) zostanie ponownie podłączone przez użytkownika lub innego Gremlina.
Jaka jest najlepsza praktyka radzenia sobie z tym?
Jeśli plik zostanie przeniesiony (w tym samym systemie plików) lub zmieniona jego nazwa, uchwyt pliku pozostanie otwarty i nadal może być używany do odczytu i zapisu pliku.
Jeśli plik zostanie usunięty, uchwyt pliku pozostaje otwarty i nadal można go używać (nie jest to oczekiwane przez niektórych). Plik nie zostanie tak naprawdę usunięty, dopóki ostatni uchwyt nie zostanie zamknięty.
Jeśli plik zostanie zastąpiony nowym plikiem, zależy to dokładnie w jaki sposób. Jeśli zawartość pliku zostanie nadpisana, uchwyt pliku będzie nadal ważny i będzie miał dostęp do nowej zawartości. Jeśli istniejący plik zostanie odłączony i utworzony nowy o tej samej nazwie lub, jeśli nowy plik zostanie przeniesiony do istniejącego pliku za pomocą rename(), jest to to samo, co usunięcie (patrz powyżej) - to znaczy uchwyt pliku będzie nadal odnosił się do oryginalna wersja pliku.
Ogólnie rzecz biorąc, kiedy plik jest otwarty, plik jest otwarty i nikt nie zmieniając struktury katalogów nie może tego zmienić - mogą przenieść, zmienić nazwę pliku lub umieścić coś innego w jego miejscu, po prostu pozostaje otwarty.
W Uniksie nie ma tylko usuwania, unlink()co ma sens, ponieważ niekoniecznie usuwa plik - po prostu usuwa link z katalogu.
Z drugiej strony, jeśli urządzenie bazowe zniknie (np. Odłączenie USB), wtedy uchwyt pliku nie będzie już poprawny i prawdopodobnie spowoduje IO / błąd przy dowolnej operacji. Jednak nadal musisz go zamknąć. Będzie to prawdą, nawet jeśli urządzenie zostanie ponownie podłączone, ponieważ w tym przypadku nie jest rozsądne pozostawienie otwartego pliku.
Przypuszczam, że twój drugi punkt ma takie samo zastosowanie, jeśli katalog zawierający plik zostanie usunięty. Czy tak jest?
Drew Noakes
2
Interesuje mnie jedno: jeśli używasz polecenia cp do nadpisania pliku, czy jest to pierwszy przypadek, czy drugi przypadek?
xuhdev
1
„ Plik nie zostanie w rzeczywistości usunięty, dopóki ostatni uchwyt nie zostanie zamknięty ”. dzięki
Geremia
8
Uchwyty plików wskazują na i-węzeł, a nie na ścieżkę, więc większość scenariuszy nadal działa tak, jak zakładasz, ponieważ uchwyt nadal wskazuje na plik.
W szczególności w przypadku scenariusza usuwania - funkcja nie bez powodu nazywana jest „odłączaniem”, niszczy ona „łącze” między nazwą pliku (dentry) a plikiem. Po otwarciu pliku, a następnie odłączeniu go, plik faktycznie istnieje, dopóki jego liczba odwołań nie spadnie do zera, czyli po zamknięciu uchwytu.
Edycja: W przypadku sprzętu otworzyłeś uchwyt do określonego węzła urządzenia, jeśli odłączysz urządzenie, jądro nie powiedzie dostępu do niego, nawet jeśli urządzenie wróci. Będziesz musiał zamknąć urządzenie i ponownie je otworzyć.
Nie jestem pewien co do innych operacji, ale jeśli chodzi o usuwanie: usuwanie po prostu nie ma miejsca (fizycznie, tj. W systemie plików), dopóki ostatnie otwarte dojście do pliku nie zostanie zamknięte. W związku z tym usunięcie pliku z aplikacji nie powinno być możliwe.
Kilka aplikacji (które nie przychodzą na myśl) polega na tym zachowaniu, tworząc, otwierając i natychmiast usuwając pliki, które następnie działają dokładnie tak długo, jak aplikacja - pozwalając innym aplikacjom być świadomym cyklu życia pierwszej aplikacji bez konieczności spójrz na mapy procesów i tym podobne.
Możliwe, że podobne uwagi mają zastosowanie do innych rzeczy.
Jaki jest sens if(!fcntl(fd, F_GETFL)) {sprawdzania? Myślę, że szukasz EBADFtam. (Prawdopodobnie zapomniałeś również zainicjować errnona 0).
wokalny
To nie działa dla mnie. Próbowałem użyć tego podejścia z open(O_WRONLY|O_APPEND)- st_nlink zawsze pozostaje> = 1, gdy mój deskryptor jest otwarty.
imbearr
2
Informacje w pamięci o usuniętym pliku (wszystkie podane przykłady są wystąpieniami usuniętego pliku), a także i-węzły na dysku pozostają w pamięci do momentu zamknięcia pliku.
Podłączanie sprzętu podczas pracy to zupełnie inny problem i nie należy oczekiwać, że program będzie działał długo, jeśli i-węzły lub metadane na dysku w ogóle ulegną zmianie .
W katalogu / proc / znajdziesz listę wszystkich aktualnie aktywnych procesów, po prostu znajdź swój PID i wszystkie dotyczące go dane. Interesującą informacją jest folder fd /, znajdziesz wszystkie programy obsługi plików aktualnie otwarte przez proces.
W końcu znajdziesz symboliczne łącze do swojego urządzenia (pod / dev / lub nawet / proc / bus / usb /), jeśli urządzenie się zawiesi, łącze będzie martwe i nie będzie można odświeżyć tego uchwytu, proces musi zostać zamknięty i otwórz go ponownie (nawet po ponownym połączeniu)
Ten kod może odczytać aktualny status łącza PID
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
int main() {
// the directory we are going to open
DIR *d;
// max length of strings
int maxpathlength=256;
// the buffer for the full path
char path[maxpathlength];
// /proc/PID/fs contains the list of the open file descriptors among the respective filenames
sprintf(path,"/proc/%i/fd/",getpid() );
printf("List of %s:\n",path);
struct dirent *dir;
d = opendir(path);
if (d) {
//loop for each file inside d
while ((dir = readdir(d)) != NULL) {
//let's check if it is a symbolic link
if (dir->d_type == DT_LNK) {
const int maxlength = 256;
//string returned by readlink()
char hardfile[maxlength];
//string length returned by readlink()
int len;
//tempath will contain the current filename among the fullpath
char tempath[maxlength];
sprintf(tempath,"%s%s",path,dir->d_name);
if ((len=readlink(tempath,hardfile,maxlength-1))!=-1) {
hardfile[len]='\0';
printf("%s -> %s\n", dir->d_name,hardfile);
} else
printf("error when executing readlink() on %s\n",tempath);
}
}
closedir(d);
}
return 0;
}
Ten końcowy kod jest prosty, możesz bawić się funkcją linkat.
Uchwyty plików wskazują na i-węzeł, a nie na ścieżkę, więc większość scenariuszy nadal działa tak, jak zakładasz, ponieważ uchwyt nadal wskazuje na plik.
W szczególności w przypadku scenariusza usuwania - funkcja nie bez powodu nazywana jest „odłączaniem”, niszczy ona „łącze” między nazwą pliku (dentry) a plikiem. Po otwarciu pliku, a następnie odłączeniu go, plik faktycznie istnieje, dopóki jego liczba odwołań nie spadnie do zera, czyli po zamknięciu uchwytu.
Edycja: W przypadku sprzętu otworzyłeś uchwyt do określonego węzła urządzenia, jeśli odłączysz urządzenie, jądro nie powiedzie dostępu do niego, nawet jeśli urządzenie wróci. Będziesz musiał zamknąć urządzenie i ponownie je otworzyć.
źródło
Nie jestem pewien co do innych operacji, ale jeśli chodzi o usuwanie: usuwanie po prostu nie ma miejsca (fizycznie, tj. W systemie plików), dopóki ostatnie otwarte dojście do pliku nie zostanie zamknięte. W związku z tym usunięcie pliku z aplikacji nie powinno być możliwe.
Kilka aplikacji (które nie przychodzą na myśl) polega na tym zachowaniu, tworząc, otwierając i natychmiast usuwając pliki, które następnie działają dokładnie tak długo, jak aplikacja - pozwalając innym aplikacjom być świadomym cyklu życia pierwszej aplikacji bez konieczności spójrz na mapy procesów i tym podobne.
Możliwe, że podobne uwagi mają zastosowanie do innych rzeczy.
źródło
jeśli chcesz sprawdzić, czy program obsługi plików (deskryptor pliku) jest w porządku, możesz wywołać tę funkcję.
źródło
if(!fcntl(fd, F_GETFL)) {
sprawdzania? Myślę, że szukaszEBADF
tam. (Prawdopodobnie zapomniałeś również zainicjowaćerrno
na 0).open(O_WRONLY|O_APPEND)
- st_nlink zawsze pozostaje> = 1, gdy mój deskryptor jest otwarty.Informacje w pamięci o usuniętym pliku (wszystkie podane przykłady są wystąpieniami usuniętego pliku), a także i-węzły na dysku pozostają w pamięci do momentu zamknięcia pliku.
Podłączanie sprzętu podczas pracy to zupełnie inny problem i nie należy oczekiwać, że program będzie działał długo, jeśli i-węzły lub metadane na dysku w ogóle ulegną zmianie .
źródło
Poniższy eksperyment pokazuje, że odpowiedź MarkR jest poprawna.
code.c:
dane:
Użyj
gcc code.c
do produkcjia.out
. Biegnij./a.out
. Gdy zobaczysz następujący wynik:Użyj,
rm data
aby usunąćdata
. Ale./a.out
będzie nadal działać bez błędów i generuje następujący wynik:Zrobiłem eksperyment na Ubuntu 16.04.3.
źródło
W katalogu / proc / znajdziesz listę wszystkich aktualnie aktywnych procesów, po prostu znajdź swój PID i wszystkie dotyczące go dane. Interesującą informacją jest folder fd /, znajdziesz wszystkie programy obsługi plików aktualnie otwarte przez proces.
W końcu znajdziesz symboliczne łącze do swojego urządzenia (pod / dev / lub nawet / proc / bus / usb /), jeśli urządzenie się zawiesi, łącze będzie martwe i nie będzie można odświeżyć tego uchwytu, proces musi zostać zamknięty i otwórz go ponownie (nawet po ponownym połączeniu)
Ten kod może odczytać aktualny status łącza PID
Ten końcowy kod jest prosty, możesz bawić się funkcją linkat.
źródło