Chciałbym szczegółowo wiedzieć o blokadach Linuksa; czy ktoś mógłby mi je wyjaśnić?
Blokada spinowa to sposób ochrony udostępnionego zasobu przed modyfikacją przez dwa lub więcej procesów jednocześnie. Pierwszy proces, który próbuje zmodyfikować zasób, „przejmuje” blokadę i kontynuuje swoją drogę, robiąc to, czego potrzebował z zasobem. Wszelkie inne procesy, które następnie próbują uzyskać blokadę, zostają zatrzymane; Mówi się, że „obracają się w miejscu”, czekając na zamek, który zostanie zwolniony przez pierwszy proces, stąd nazwa spin lock.
Jądro Linux używa blokad spinowych do wielu rzeczy, na przykład podczas wysyłania danych do określonego urządzenia peryferyjnego. Większość sprzętowych urządzeń peryferyjnych nie jest zaprojektowana do obsługi wielu jednoczesnych aktualizacji stanu. Jeśli muszą wystąpić dwie różne modyfikacje, jedna musi ściśle przestrzegać drugiej, nie mogą się one nakładać. Blokada obrotowa zapewnia niezbędną ochronę, zapewniając, że modyfikacje są wprowadzane pojedynczo.
Blokady spinowe stanowią problem, ponieważ wirujące bloki rdzenia procesora tego wątku nie wykonują żadnej innej pracy. Podczas gdy jądro Linuksa zapewnia usługi wielozadaniowości działającym pod nim programom użytkownika, ta uniwersalna funkcja wielozadaniowości nie obejmuje kodu jądra.
Ta sytuacja się zmienia i tak było przez większość istnienia Linuksa. W Linuksie jądro było prawie wyłącznie programem jednozadaniowym: za każdym razem, gdy procesor wykonywał kod jądra, używany był tylko jeden rdzeń procesora, ponieważ istniała jedna blokada spinowa chroniąca wszystkie współdzielone zasoby, zwana Big Kernel Lock (BKL ). Począwszy od Linuksa 2.2, BKL powoli dzieli się na wiele niezależnych blokad, z których każda chroni bardziej skoncentrowaną klasę zasobów. Dzisiaj, z jądrem 2.6, BKL nadal istnieje, ale jest używany tylko przez naprawdę stary kod, którego nie można łatwo przenieść do bardziej szczegółowej blokady. Obecnie jest całkiem możliwe, że w pudełku wielordzeniowym każdy procesor działa użyteczny kod jądra.
Ograniczenie użyteczności BKL jest ograniczone, ponieważ jądro Linuksa nie ma ogólnej wielozadaniowości. Jeśli rdzeń procesora zostanie zablokowany podczas wirowania w blokadzie spinania jądra, nie można go powtórzyć, aby zrobić coś innego, dopóki blokada nie zostanie zwolniona. Po prostu siedzi i obraca się, aż zamek zostanie zwolniony.
Blokady obrotowe mogą skutecznie przekształcić potworną 16-rdzeniową skrzynkę w jedno-rdzeniową skrzynkę, jeśli obciążenie jest takie, że każdy rdzeń zawsze czeka na pojedynczy blokadę wirowania. Jest to główny limit skalowalności jądra Linuksa: podwojenie rdzeni procesora z 2 do 4 prawdopodobnie prawie podwoi prędkość Linux-a, ale podwojenie go z 16 do 32 prawdopodobnie nie przy większości obciążeń.
Blokada spin ma miejsce, gdy proces nieustannie sprawdza, czy blokada ma zostać usunięta. Jest to uważane za złe, ponieważ proces zużywa cykle (zwykle) niepotrzebnie. Nie jest on specyficzny dla Linuksa, ale ogólny wzorzec programowania. I chociaż jest to ogólnie uważane za złą praktykę, jest w rzeczywistości właściwym rozwiązaniem; zdarzają się przypadki, w których koszt korzystania z harmonogramu jest wyższy (pod względem cykli procesora) niż koszt kilku cykli, które ma trwać spinlock.
Przykład spinlocka:
Często istnieje sposób na uniknięcie blokady wirowania. W tym konkretnym przykładzie istnieje narzędzie Linux o nazwie inotifywait (zwykle nie jest instalowane domyślnie). Gdyby został napisany w C, wystarczy użyć interfejsu API inotify, który zapewnia Linux.
Ten sam przykład przy użyciu inotifywait pokazuje, jak osiągnąć to samo bez blokady wirowania:
źródło
Gdy wątek próbuje uzyskać blokadę, mogą się zdarzyć trzy rzeczy, jeśli się nie powiedzie, może spróbować zablokować, może spróbować i kontynuować, może następnie przejść do trybu uśpienia, informując system operacyjny o przebudzeniu, gdy nastąpi jakieś zdarzenie.
Teraz spróbuj i kontynuuj zajmuje dużo mniej czasu niż próbuj i blokuj. Powiedzmy na chwilę, że „spróbuj i kontynuuj” zajmie mi jednostkę czasu, a „spróbuj i zablokuj” zajmie sto.
Teraz załóżmy na chwilę, że przytrzymanie zamka zajmuje średnio 4 jednostki czasu. Nie warto czekać na 100 jednostek. Zamiast tego piszesz pętlę „spróbuj i kontynuuj”. Przy czwartej próbie zwykle zdobędziesz zamek. To jest blokada obrotu. Nazywa się to, ponieważ nić obraca się w miejscu, dopóki nie dostanie zamka.
Dodatkowym środkiem bezpieczeństwa jest ograniczenie liczby uruchomień pętli. Tak więc w tym przykładzie wykonujesz pętlę for, np. Sześć razy, jeśli zawiedzie, wówczas „próbujesz zablokować”.
Jeśli wiesz, że wątek zawsze blokuje na przykład 200 jednostek, to marnujesz czas komputera na każdą próbę i kontynuowanie.
W końcu blokada spinowa może być bardzo wydajna lub marnotrawiona. Jest to marnotrawstwem, gdy „typowy” czas na trzymanie zamka jest dłuższy niż czas potrzebny na „próbę zablokowania”. Jest skuteczny, gdy typowy czas trzymania zamka jest znacznie krótszy niż czas „próby zablokowania”.
Ps: Książką do przeczytania w wątkach jest „Elementarz wątku”, jeśli nadal możesz go znaleźć.
źródło
Blokada jest sposobem dla dwóch lub więcej zadań (procesów, wątków) zsynchronizować. W szczególności, gdy oba zadania wymagają przerywanego dostępu do zasobu, z którego może korzystać tylko jedno zadanie naraz, jest to sposób, aby zadania nie wykorzystywały zasobu w tym samym czasie. Aby uzyskać dostęp do zasobu, zadanie musi wykonać następujące kroki:
Przejęcie blokady nie jest możliwe, jeśli już zajęło ją inne zadanie. (Pomyśl o zamku jak o fizycznym tokenie. Albo obiekt znajduje się w szufladzie, albo ktoś ma go w ręce. Tylko osoba trzymająca przedmiot może uzyskać dostęp do zasobu.) Zatem „zabranie zamka” naprawdę oznacza „poczekaj, aż nikt inny nie ma zamka, więc weź go ”.
Z wysokiego punktu widzenia istnieją dwa główne sposoby implementacji blokad: blokady spinowe i warunki. W przypadku spinlocków zabranie blokady oznacza po prostu „wirowanie” (tzn. Nie robienie nic w pętli), dopóki nikt inny nie będzie miał blokady. W pewnych warunkach, jeśli zadanie próbuje przejąć blokadę, ale jest zablokowane, ponieważ inne zadanie ją przechowuje, nowicjusz wchodzi do kolejki oczekiwania; operacja zwolnienia sygnalizuje każdemu oczekującemu zadaniu, że blokada jest już dostępna.
(Te wyjaśnienia nie są wystarczające, aby wdrożyć blokadę, ponieważ nie mówiłem nic o atomowości. Ale atomowość nie jest tutaj ważna.)
Spinlocki są oczywiście marnotrawstwem: zadanie oczekiwania sprawdza, czy blokada jest zajęta. Dlaczego i kiedy jest używany? Spinlocki są często bardzo tanie w przypadku, gdy zamek nie jest trzymany. Dzięki temu jest atrakcyjny, gdy szansa na zablokowanie zamka jest niewielka. Co więcej, blokady spinowe są opłacalne tylko wtedy, gdy nie oczekuje się, że uzyskanie blokady zajmie dużo czasu. Blokady spinowe są zwykle używane w sytuacjach, w których będą trzymane przez bardzo krótki czas, dzięki czemu większość prób zakończy się sukcesem przy pierwszej próbie, a te, które wymagają oczekiwania, nie czekają długo.
Istnieje dobre wytłumaczenie blokad i innych mechanizmów współbieżności jądra Linux w Sterownikach urządzeń Linux , rozdział 5.
źródło
synchronized
aby został zaimplementowany przezsynchronized
blokadę : blok mógłby działać bardzo długo.synchronized
jest konstrukcją językową ułatwiającą korzystanie z blokad w niektórych przypadkach, a nie prymitywną do budowania większych operacji podstawowych synchronizacji.Blokada jest blokadą, która działa poprzez wyłączenie harmonogramu i ewentualnie przerywa (wariant irqsave) na tym konkretnym rdzeniu, na którym blokada została nabyta. Różni się od mutexa tym, że wyłącza planowanie, więc tylko twój wątek może działać, gdy spinlock jest trzymany. Muteks pozwala na planowanie innych wątków o wyższym priorytecie, gdy jest on trzymany, ale nie pozwala im na jednoczesne wykonywanie chronionej sekcji. Ponieważ blokady blokują wyłączanie wielozadaniowości, nie można pobrać blokady, a następnie wywołać innego kodu, który będzie próbował uzyskać muteks. Twój kod wewnątrz sekcji blokady nie może nigdy spać (kod zwykle śpi, gdy napotka zablokowany muteks lub pusty semafor).
Inną różnicą w przypadku muteksu jest to, że wątki zwykle ustawiają się w kolejce dla muteksu, więc muteks pod spodem ma kolejkę. Natomiast spinlock zapewnia, że żaden inny wątek nie będzie działał, nawet jeśli będzie musiał. Dlatego nigdy nie wolno blokować podczas wywoływania funkcji spoza pliku, co do których nie masz pewności, że nie będzie spać.
Jeśli chcesz podzielić blokadę z przerwaniem, musisz użyć wariantu irqsave. To nie tylko wyłączy harmonogram, ale także wyłączy przerwania. To ma sens, prawda? Spinlock działa, upewniając się, że nic więcej nie będzie działać. Jeśli nie chcesz, aby przerwanie było uruchamiane, wyłącz je i przejdź bezpiecznie do sekcji krytycznej.
Na maszynie wielordzeniowej spinlock rzeczywiście się obraca, czekając na kolejny rdzeń, który trzyma zamek, aby go zwolnić. To wirowanie odbywa się tylko na maszynach wielordzeniowych, ponieważ na komputerach z jednym rdzeniem nie może się tak zdarzyć (albo przytrzymujesz spinlock i kontynuujesz, albo nigdy nie biegniesz, dopóki blokada nie zostanie zwolniona).
Spinlock nie jest marnotrawstwem tam, gdzie ma to sens. W przypadku bardzo małych sekcji krytycznych niepotrzebne byłoby przydzielanie kolejki zadań mutex w porównaniu do zwykłego zawieszenia harmonogramu na kilka mikrosekund potrzebnych do zakończenia ważnej pracy. Jeśli musisz spać lub przytrzymać blokadę podczas operacji io (która może spać), użyj mutex. Z pewnością nigdy nie blokuj blokady, a następnie spróbuj zwolnić ją w przerwie. Chociaż to zadziała, będzie to jak arduino bzdura while (flagnotset); w takim przypadku użyj semafora.
Złap blokadę, gdy potrzebujesz prostego wzajemnego wykluczenia bloków transakcji pamięciowych. Chwyć muteks, gdy chcesz zatrzymać wiele wątków tuż przed blokadą muteksu, a następnie wybierz wątek o najwyższym priorytecie, aby kontynuować, gdy muteks stanie się wolny i kiedy zablokujesz i zwolnisz w tym samym wątku. Chwyć semafor, jeśli zamierzasz opublikować go w jednym wątku lub przerwie i weź go w innym wątku. Są trzy nieco inne sposoby zapewnienia wzajemnego wykluczenia i są one wykorzystywane do nieco innych celów.
źródło