Pytanie Wdrażanie pakietu wątków na poziomie użytkownika


Zostałem poproszony w klasie o utworzenie biblioteki wątków na poziomie użytkownika w C. Zastanawiałem się, czy ktoś mógłby dać mi listę rzeczy do przeczytania, aby to osiągnąć. Mam dobry pomysł, od czego zacząć, ale wszelkie zasoby na temat wątków na poziomie użytkownika i niektóre stosowne aspekty języka C, które mogłyby pomóc, byłyby niezwykle cenne.

Jestem bardzo niejasne, w jaki sposób mogę wdrożyć harmonogram dla takich. Załóżmy, że dobrze rozumiem język C i niektóre z jego bardziej przydatnych funkcji bibliotecznych.


16
2018-02-04 22:48


pochodzenie


Przypisanie jest trochę trudne do spełnienia, ponieważ nie można tego zrobić "w C". Potrzebujesz co najmniej minimalnej ilości zespołów lub odpowiedników rozszerzeń kompilatora, aby ułatwić tworzenie nowych kontekstów wykonania i przełączać się między nimi. Albo możesz napisać własną kompletną maszynę wirtualną i implementację C do uruchomienia na maszynie wirtualnej, ale nie sądzę, że to jest to, co miał na myśli Twój instruktor ... - R..
W przypadku wielozadaniowości kooperacyjnej / wątku przydatne mogą okazać się funkcje getcontext / makecontext / setcontext. - MetallicPriest
Nie widzę, jak można to zrobić bez jakiegoś assemblera do implementacji zapisywania / przywracania kontekstu rejestru i wskaźnika stosu. To bez nawet rozważania I / O i jak na to czekać. - Martin James
@MartinJames, jeśli zawartość jmp_buf są udokumentowane w twoim systemie, możesz zapisać i przywrócić cały potrzebny kontekst, zawijając setjmp/longjmp. Zrobiłem to. (Kod podszedł do wspaniałego /dev/null na niebie, więc nie mogę tego wskazać. ) - J. C. Salomon


Odpowiedzi:


Zrobiłem to dla zadania domowego bez pisania jakiegokolwiek asemblera. Mechanizm przełączania wątków był setjmp/longjmp. Wiązało się to z przydzieleniem pamięci dla każdego stosu wątku, a następnie bardzo ostrożnym masowaniem wartości w pliku jmp_buff więc wykonanie przechodzi do stosu następnego wątku.

Zobacz także Russ Cox w całkiem czytelny sposób libtask.

Edytuj w odpowiedzi na komentarz OP: Przy podejmowaniu decyzji o zmianie wątków istnieją dwa główne kierunki: preemptive i cooperative. W modelu wyprzedzającym, będziesz miał coś w rodzaju sygnału timera, który powoduje, że przepływ wykonania przeskoczy do głównego wątku programu rozsyłającego, który wybiera następny wątek do uruchomienia. W modelu współpracy wątki "ustępują" sobie nawzajem, albo jawnie (na przykład, dzwoniąc do a yield() funkcja, którą podasz) lub pośrednio (na przykład, żądając blokady posiadanej przez inny wątek).

Zapoznaj się z API libtask na przykład modelu współpracy, w szczególności opis funkcji taskyield(). To wyraźny plon, o którym wspomniałem. Istnieją również nieblokujące funkcje wejścia / wyjścia, które zawierają ukrytą wydajność - bieżące "zadanie" zostaje wstrzymane do czasu zakończenia operacji wejścia / wyjścia, ale pozostałe zadania mają szansę na uruchomienie.


8
2018-02-05 00:20



Tak wyobrażałem sobie, że będę przełączał wątki. Przypuszczam, że moim największym brakiem wiedzy jest ustalenie, kiedy wątek powinien zrezygnować z kontroli, aby inny mógł działać. tj. w jaki sposób wątki decydują o tym, że mają wystarczająco dużo czasu. - SirensOfTitan
@SirensOfTitan, zaktualizowałem swoją odpowiedź, aby zasygnalizować to, o co prosiłeś. - J. C. Salomon


Prostego harmonogramu współpracy można zrobić w C przy użyciu swapcontext, spójrz na przykład na stronie man swapcontext tutaj, to jest jego wynik:

$ ./a.out
main: swapcontext(&uctx_main, &uctx_func2)
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: exiting

Jak widać, jest to całkiem wykonalne.

Uwaga: jeśli zamienisz kontekst wewnątrz programu obsługi sygnału timera, będziesz miał plan działania zapobiegawczego, ale nie jestem pewien, czy jest to bezpieczne, czy możliwe.

Edycja: Znalazłem to na stronie man sigaction, która sugeruje, że możliwe jest przełączenie kontekstu wewnątrz procedury obsługi sygnału:

Jeśli SA_SIGINFO jest określone w sa_flags, to sa_sigaction (zamiast   sa_handler) określa funkcję obsługi sygnału dla signum.   Ta funkcja odbiera numer sygnału jako swój pierwszy argument, a   wskaźnik do siginfo_t jako drugi argument i wskaźnik do a   ucontext_t (rzucany do pustki *) jako trzeci argument.


4
2018-02-04 23:21



Biorąc pod uwagę, że jest to problem związany z zadaniami domowymi (a ja go ponownie zapisałem), PO powinien sprawdzić, czy byłoby to do przyjęcia. Bardziej prawdopodobne jest, że instruktor szuka implementacji swapcontext() funkcjonalność, a nie po prostu go używać. - J. C. Salomon


Możesz zajrzeć do implementacji open source firmy Apple. Zauważ, że największa część kodu jest w rzeczywistości zbiorem, ponieważ wymaga pewnych specjalistycznych rzeczy, których nie możesz wykonać w C, takich jak pobieranie adresu powrotu ramki stosu lub przeskakiwanie do dowolnego adresu.

Nitki krajów użytkownika (zwane również powszechnie "włóknami") zazwyczaj wykorzystują model kooperatywny; to znaczy wątki są wykonywane, dopóki nie zdecydują, że mają wystarczająco dużo czasu, a następnie poddają się innemu wątkowi. Za pomocą kolejki priorytetowej można zaimplementować program planujący, który wykonuje zadanie, które zostało uruchomione przez najkrótszy czas. (Program planujący śledzi bieżące zadania, a uruchomione zadanie zwraca się, gdy uzna, że ​​jest wystarczająco dużo, a program planujący aktualizuje czas wykonania zadania, a następnie generuje czas, który miał najmniejszy czas wykonania).


1
2018-02-04 23:32