Komentarze do LABu 4

Organizacyjne

Przypominam o 1. zadaniu zaliczeniowym.

W tym tygodniu pojawi się (pojawiło się) 2. zdanie zaliczeniowe. Dotyczy asemblera i współbieżności: tematy zeszło- i tego-tygodniowe.

Na początku

Warto przypomnieć sobie z Programowania Współbieżnego, czym jest wątek. Na dzisiejszych zajęciach trzeba pamiętać, że wątki jednego procesu współdzielą pamięć (czyli wszystkie mają dostęp do zmiennych globalnych), ale każdy ma oddzielny stos i wartości rejestrów: każdy wątek widzi tylko swój stos, a w rejestrach tylko wartości, które sam wpisał. (Jak to jest realizowane, będziemy omawiać podczas zajęć z MINIX-em).

Sekcja 1

Dyrektywa align <wartość> powoduje, że adres tego miejsca (a dokładniej, adres początku instrukcji/miejsca umieszczonego za tę dyrektywą) będzie wyrównany do <wartość> bajtów. Odpowiednie wyrównanie odpowiednich fragmentów (zmienne, adresy docelowe skoków) może sprawić, że wykonanie będzie szybsze. Czy faktycznie będzie i o ile, to może zależeć od konkretnego procesora – warto poeksperymentować.

Funkcja inc_thread() jest wołana z C, więc musi przestrzegać konwencji języka C (zob. poprzednie zajęcia). Warto zacząć od przeczytania i zrozumienia pliku inc_thread_test.c, w którym ta funkcja jest wołana.

Warto zauważyć, że o ile pisząc kod np. w C naturalnie zaimplementowalibyśmy pętlę zwiększającą zmienną globalną jako pętlę while-do, czyli w kolejności spr. warunku – ciało pętli – skok do spr. warunku, to w podanym kodzie asemblerowym warunek pętli jest umieszczony po ciele pętli, więc jest to układ do-while: ciało pętli – spr. warunku – ew. skok do ciała. Jest to tylko układ jak do-while, bo wykonanie jest nadal jak while-do: na początku przeskakujemy ciało pętli (jmp count_test), aby najpierw sprawdzić warunek pętli i dopiero jeśli jest on spełniony, to skaczemy do góry, do ciała pętli. Taki układ kodu pętli, choć wydaje się być mniej naturalny, jest ,,standardem’’ w asemblerze i większość kompilatorów właśnie tak kompiluje pętle. Dlaczego? Polecam np. to pytanie na SO .

W podanym kodzie wartość jest zwiększana za pomocą operacji inc dword [rsi]. To dword nie jest argumentem operacji inc. Jest ono podpowiedzią dla asemblera, że ma on wygenerować takie inc, które odczytuje i zwiększa 32-bitową liczbę zapisaną pod adresem trzymanym w rsi. Bez dword asembler nie ma tu jak domyśleć się, czy chodzi nam o liczbę 8-, 16-, 32-, a może 64-bitową.

Należy zauważyć, że inc dword [rsi] wykonuje 2 odwołania do pamięci: odczytuje wartość z pamięci, zwiększą ją i zapisuje zwiększoną wartość do pamięci. Zatem jest tu klasyczny problem ze współbieżnością: zanim jeden wątek zapisze zwiększoną wartość, drugi wątek może zdążyć doczytać starą wartość, którą też zwiększy i zapisze.

Sekcja 1.1

Użycie \ w kodzie, to jest złamanie linii. Dzięki temu możemy (np. dla czytelności) zapisać coś w 2 liniach, ale dla programu będzie to dalej ta sama linia. Np.

repne \
scasb

NASM interpretuje, jakby było zapisane w jednej linii:

repne scasb

Sekcja 1.2

Warto przypomnieć sobie z Programowania Współbieżnego, które (dwie) operacje muszą zostać wykonane atomowo, aby spinlock rzeczywiście działał. W dalszej części tego scenariusza będą rozważane różne sposoby (instrukcje), które mogą dać nam właśnie takie działanie.

Sekcja 1.3

To ZF (Zero Flag) to jest właśnie jedna z flag, o których mówiliśmy na poprzednich zajęciach, jak omawialiśmy, jak realizowane są w asemblerze skoki warunkowe (“if’y”). Warto zajrzeć do tabelek Instrukcje warunkowe załączonych do zeszłotygodniowego tematu laboratorium.