Grand Central Dispatch we FreeBSD
- Dodano: 17 października 2009
- Wprowadził: krzy3
- Komentarze: 38
Grand Central Dispatch, czyli system zarządzania wątkami wprowadzony przez Apple w Mac OS X 10.6, będzie włączony do przyszłego wydania FreeBSD (8.1).
Najkrócej mówiąc, GCD to biblioteka ułatwiająca tworzenie aplikacji wielowątkowych. W przeciwieństwie jednak do dotychczasowych rozwiązań, programista używający libgcd nie tworzy sam wątków, a jedynie definiuje zadania (funkcje) do wykonania i umieszcza je w kolejce. System natomiast pobiera zadania z kolejek i automatycznie decyduje kiedy i ile wątków przydzielić do danego zadania. Rozwiązanie takie umożliwia zmniejszenie ilości działających jednocześnie w systemie wątków, a co za tym idzie oszczędność pamięci. Dodatkową zaletą jest to, że programista nie musi się zastanawiać, ile wątków utworzyć aby wykorzystać wszystkie procesory (rdzenie) w systemie — system zrobi to automatycznie, w zależności od konfiguracji komputera i chwilowego obciążenia.
Czytelnikom zainteresowanym bliżej działaniem GCD polecamy dokładniejszy opis oraz dokumentację biblioteki.
Grand Central Dispatch został opracowany przez Apple i stanowi część systemu operacyjnego Mac OS X 10.6 Snow Leopard. Projekt został nastepnie opublikowany przez Apple na licencji Apache 2.0. 26 września poinformowano, że portowanie biblioteki na FreeBSD zakończyło się sukcesem. Ostatnio ogłoszono, że po wydaniu wersji 8.0 systemu (obecnie na etapie RC1) kod libgcd będzie przeniesiony z gałęzi rozwojowej systemu (9.0-CURRENT) do gałęzi stabilnej i stanie się częścią wydania 8.1. Planowane jest również przeanalizowanie istniejącego oprogramowania systemowego pod kątem korzyści, jakie może w nim dać zastosowanie GCD.
Więcej informacji: http://www.osnews.com/comments/22331
Znalazłeś literówkę? Zgłoś ją używając formularza!
Jeśli uważasz, że ten nius jest nieobiektywny, przedstawia nieprawdziwe wydarzenie, jest spamem lub nie spełnia standardów serwisu, napisz raport.
Niusy na podobny temat:
Komentarze są prywatnymi opiniami dodających je osób. Prosimy o zachowanie kultury wypowiedzi. Komentarze obraźliwe oraz obniżające poziom serwisu będą usuwane. Więcej w regulaminie komentowania.
38 komentarzy
Wszystkie autorskie niusy w serwisie publikowane są na licencji Creative Commons Uznanie autorstwa 2.5 Polska.


Warto nadmienić, że identyczną niemal funkcjonalność dla większości cywilizowanych systemów operacyjnych zapewniają Intel TBB, i że one w dodatku już są używane w licznych praktycznych aplikacjach
Czyli znów odkrycie koła.
Nie jest identyczne, bo obejmuje tylko c++.
Zastanawiam się tylko czy we FBSD można używać tego niestandardowego rozszerzenia języków "BLOCK". Bez niego GCD wygląda trochę niepełnosprawne, a z tego co wiem ci bardziej konserwatywni programiści są do niego nastawieni mocno na "nie".
GCD obejmuje tylko C, C++ i objective C… duża różnica jakościowa to nie jest. C można żałować, objective C to egzotyka i tak niezbyt upowszechniona (porównać statystyki C, C++ i Objective C na ohloh).
Natomiast otwarte rozwiązanie Intela działa na conajmniej 4 różnych kompilatorach (alfabetycznie: gcc, intel, microsoft, sun) i jego stosowanie nie wymaga istotnych zmian systemu operacyjnego, co jest cenne
Oczywiście ani rozwiązanie Apple ani Intela nie ma wsparcia dla Nowoczesnych Języków/Środowisk Programistycznych, ale to nie jest problem, bo ich użytkownicy i tak 3/4 czasu zajmują się poznawaniem 3-4-literowych skrótów pokroju ADO, ASP, CIL, CLR, EJB, JAAS, JAXB, JAXP, JDBC, JMS, JPQL, JSF, LINQ, NET, WCF i WPF i na programowanie im czasu nie starcza… ale to osobna historia.
@maciek "GCD obejmuje tylko C, C++ i objective C… duża różnica jakościowa to nie jest. C można żałować, objective C to egzotyka i tak niezbyt upowszechniona (porównać statystyki C, C++ i Objective C na ohloh)."
GCD działa też w najnowszej implementacji MacRuby (nota bene napisanej w Obj-C) i czyni z niego wydajne rozwiązanie programistyczne (jest wolniejsze od C++, ale bywa szybsze od samego Obj-C, warto też wspomnieć, że skrypty MacRuby można kompilować do postaci czysto binarnej).
Nie wiem skąd taka niechęć do języków wyższego poziomu. Wiele z nich umożliwia napisanie funkcjonalnej aplikacji w tydzień i to przez jednego programistę. W przypadku C i C++ problemem zwykle jest szybkie oprogramowanie interfejsu użytkownika – w .NET 3.5 pod VS stworzenie dobrego interfejsu zajmuje znacznie krócej.
Sam osobiście uważam, że każdy język programowanie ma swoje zastosowanie. C i C++ użyjemy kiedy zależy nam na jak najlepszej optymalizacji, natomiast C# lub JAVA, kiedy chcemy napisać szybko aplikację i nie chcemy pisać zbyt dużo kodu (w tych językach również łatwiej przystosować kod używany wcześniej – no w C++ również jest to możliwe, ale nie w takim stopniu). Całe to psioczenie na .NET, Mono i Javę jest po prostu śmieszne. Coraz nowsze technologie pojawiające się w środowisku IT mają jedynie ułatwić zadanie programiście, a nie go ogłupić. Jeżeli ktoś nie chce ich wykorzystywać – jego wybór, ma inne alternatywy. Czemu jednak insynuować, że są one gorsze. Rozwiązania te mają zdjąć z programisty brzemię dbania o szczegóły i szczególiki, które przy danym projekcie mogą być zupełnie nieistotne. Podam przykład odnośnie LINQ. Co jest szybciej napisać:
a)
List lista = inicjalizujJakasListe();
List result = new List();
for (int i = 0; i < lista.Length; i++)
if (lista[i].PoleA == jakasWartosc && lista[i].PoleB != jakasInnaWartosc)
result.Add(lista[i]);
b)
List result = inicjalizujJakasListe().Where(
(JakasKlasa jk) => jk.PoleA == jakasWartosc &&
jk.PoleB != jakasInnaWartosc).ToList();
To tylko baaardzo prosty przykład, który nawet w połowie nie pokazuje ile jesteśmy w stanie zyskać czasu na pisaniu takiego prostego kodu, a to dopiero początek możliwości LINQ – w postaci zapytań jest jeszcze zwięźlejszy (to w b to postać proceduralna), ale trzeba znać składnię trochę lepiej, aby było równie intuicyjne. W C i C++ doszło by jeszcze martwienie się o pamięć. Zakładając, że ten kod będzie używany w aplikacji biurkowej to różnica wynikająca z czasu wykonania i ewentualnego zużycia zasobów będzie dla potencjalnego użytkownika niezauważalna (nawet jakby czas wykonania był trzy razy dłuższy to 0.001s będzie odczuwane tyle samo co 0.003s).
Odnośnie innych rozwiązań się nie wypowiadam, bo nie wykorzystywałem w jakimkolwiek lub znaczącym stopniu. Myślę, że bez trudu można znaleźć programistów, którzy z ich użyciem programowali i z rezultatów są zadowoleni. Jak wspomniałem na początku, najważniejsze są założenia, bo przecież nie będziemy pisać serwera do obsługi tysięcy klientów w JAVA'ie, a małego okienkowego konwertera plików graficznych w czystym C.
Oczywiście, że serwerów obsługujących tysiące klientów nie napiszemy w javie!
Napiszemy je w perlu, tak jak LiveJournal:
Perlbal
Djabberd
Oczywiście, że serwerów obsługujących miliony klientów nie napiszemy w Perlu. Napiszemy je w scali i chodzą na JVM tak ja np Twitter.
"jol" – jeszcze, się nie nauczyłeś, że da się praktycznie wszystko we wszystkim napisać. Tylko kwestia kasy – ile mamy na programistów, sprzet, i ew. ile czasu.
Marcinie, wybacz, ale nie przeczytałeś materiałów które podlinkowałem. Napiszemy, owszem, tak jak zrobił to livejurnal.
Chodzi mi o to, że c/cpp jest wygryzany z aplikacji serwerowych przez języki takie jak java, ruby, perl i temu podobne. Z dobrymi efektami.
NDyA: nie chcę się czepiać ale ten Twój przykładowy kod można zapisać w C++ jako:
struct A {int a, b};
// …
vector< A > va;
list< A > la;
ext::copy_if(va.begin(), va.end(), back_inserter(la), (
____ bind(&A::a, _1) == 1 && bind(&A::b, _1) == 2
));
Jeszcze z pomocą Boost.Lambda ale gcc 4.5 ma już wsparcie dla lambd C++0x więc już niedługo składnia będzie przyjemniejsza. Dodatkowo ext::copy_if to mój algorytm ale napisanie go powinno zająć około 15 min. średnio rozgarniętemu programiście C++.
Oczywiście o żadną pamięć nie trzeba się martwić. No chyba, że o tą której zabraknie używając .Net.
O, popsułem osnews…
Powyżej oczywiście powinno być vector{A} i list{A} tylko z ostrymi nawiasami.
Nie znam Boost'a, więc nie za bardzo mam odniesienie. Jeżeli potrafi robić to samo co LINQ to fajnie. Natomiast zaletą tego rozwiązania jest to, że nie trzeba nic więcej deklarować i tym bardziej korzystać z enumeratorów. Środowisko samo z nich korzysta, ale nie trzeba o tym pamiętać, obsługa jest bardziej intuicyjna (szczególnie w postaci proceduralnej). Zauważ, że nie jest potrzebne żadne sztuczne podłączanie się do zmiennych – dzieje się to automatycznie, podajemy tylko nazwę zmiennej i jej typ, a LINQ podłącza nam je pod kolejne elementy tablicy. Dostęp do zmiennych i funkcji jest spod obiektu, który deklarujemy w funkcji zapytania.
Nie, Boost.Lambda służy tylko do wygodnego (w miarę) zapisu warunku. Wyszukiwanie i kopiowanie pomiędzy kontenerami to już standardowe algorytmy STL. Chodziło mi tylko o to, aby pokazać, że C++ pozwala na równie(*) wysokopoziomowy zapis jak C#. Tylko należy go umieć używać. Podobnie jak problemy z pamięcią nie występują jeśli posługujesz się nim zgrabnie.
Właśnie nie poprawnie a zgrabnie, sporą wadą C++ jest to, że poprawnie nie wystarczy. Co za tym idzie dla przeciętnego programisty Javy dobre użycie C++ może być poza zasięgiem.
(*) W gruncie rzeczy pozwala na kod znacznie wyższego poziomu, coś a la Lisp, ale to teraz nieistotne.
> czy we FBSD można używać tego niestandardowego rozszerzenia języków “BLOCK”
Zlinkowane ogłoszenie mówi wyraźnie o uruchomieniu na FreeBSD GCD z obsługą BLOCK. Wymaga to zainstalowania zmodyfikowanej wersji clang/LLVM, ale działa.
Biorąc pod uwagę, że w chwili obecnej można już skompilować działający system za pomocą clang (przynajmniej działający na x86 i x64), to widać w którą stronę to idzie…
Dziękuję. To bardzo miła wiadomość.
@krzy3: Jest tez patch do GCC dodajacy obsluge blocks. Tyle, ze patch jest do ostatniej wersji sprzed GPL3, czyli 4.2.1. Dla Apple, podobnie jak i paru innych firm, licencja nowszych wersji jest nieakceptowalna, wiec patcha dla nowych wersji GCC nie ma; z kolei kolesie od GCC nie maja zamiaru pomagac w adaptacji rozwiazan konkurencji, wiec wsparcie dla blocks w GCC nie weszloby, nawet gdyby Apple dostarczylo patcha do nowszego GCC.
@maciek: Tak gwoli scislosci, to jest to bzdura – Thread Building Blocks sluza do czegos zupelnie innego niz GCD. W najwiekszym skrocie – GCD mozna bardzo latwo i szybko uzyc do "zwatkowania" aplikacji desktopowej albo serwerowej; mozna powiedziec, ze GCD odciaza programiste od myslenia o watkach. TBB natomiast to po prostu zestaw funkcji bibliotecznych (i, bodajze, szablonow) przydatnych w pisaniu rzeczy typowo obliczeniowych.
Poza tym TBB jest dostepne na OSX (i FreeBSD) mniej wiecej od momentu wypuszczenia TBB przez Intela.
Tia… TBB, Tutorial.pdf, rozdział 11 — bezpośrednie użycie Task Schedulera. Tego odpowiednikiem jest GCD (właśnie tak, nie w drugą stronę).
Jakbyś się wypowiadał na tematy o których masz chociaż blade pojęcie to… Nic by to nie zmieniło, aby zyskać trochę powagi powinieneś pisać o rzeczach na których znasz się dobrze. A wątpię, czy takie w ogóle istnieją…
Uściślając — nie chcę mi się czekać na to czy Trasz wpadnie na taki argument czy nie. To czego nie ma w TBB a jest w libdipatch to wparcie dla ,,dispatch sources'' czyli odpalanie zadań w odpowiedzi na zdarzenie jądra. Po to używa się we fribzdi kqueue.
W Linuksie tą funkcjonalność trzeba napisać używając epoll (o żadnym portowaniu kqueue nie ma mowy) i Task Schedulera. Rzeczywiście to jest bardziej upierdliwe. Z drugiej strony nie przypominam sobie abym pisał to kiedyś ręcznie nie używając Boost.Asio.
@bies: O, w koncu doczytales. Super, bedzie sie rozmawialo latwiej.
A teraz – to, co wedlug ciebie jest 'dodatkiem', czyli libdispatch, w przypadku GCD jest glownym elementem. Wynika to wlasnie z faktu, ze TBB nadaja sie glownie do zadan obliczeniowych, a GCD – do aplikacji desktopowych i serwerowych.
A co do epoll() – problem wlasnie w tym, ze w Linuksie jest tylko epoll(), ktore nie posiada wymaganej funkcjonalnosci. Konkretnie chodzi o to, ze kqueue() moze czekac jednoczesnie na wiele rodzajow zdarzen, a epoll() – nie. Wiec Linux musialby albo najpierw dorobic sie potrzebnej infrastruktury w kernelu, albo trzebaby wladowac w libdispatch kupe hackow.
O tym co jest główną funkcjonalnością i ileż to hacków trzeba aby zmienić kilka funkcji w queue.c to raczej nie ma sensu z Tobą gadać.
Ale powiedz mi proszę o co chodzi Ci z epoll?
@bies: Problemem epoll() jest to, ze potrafi czekac tylko na filedeskryptory – ot, taki bardziej skalowalny select(2). Natomiast kqueue(2) potrafi czekac na rozne rodzaje zdarzen. W efekcie, jesli pod OSX, FreeBSD albo Windows aplikacja potrzebuje jednoczesnie czekac na filedeskryptory, timery i jakies rozne zdarzenia zewnetrzne, to ma do tego gotowy, elastyczny mechanizm (kqueue albo WaitForMultipleObjects()); w przypadku Linuksa trzeba radzic sobie hackami w rodzaju nasluchiwania na te 'dodatkowe' rzeczy w innym watku, ktory, po otrzymaniu zdarzenia, zapisuje cos do deskryptorow, na ktorych, przy uzyciu epoll(), wisi glowny watek.
Stare informacje: man eventfd, man signalfd, man timerfd_create. Pytanie co może zostać przedstawione jako fd.
@bies: No wiec prosze, podaj mi linuksowe funkcje systemowe robiace fd z, na przyklad, informacji ze dany proces sie zakonczyl, albo ze plik zostal usuniety, albo ze interfejs sieciowy sie podniosl. Bo w przypadku kqueue(2) to wszystko jest – i to bez koniecznosci dodawania kolejnych funkcji systemowych w celu 'udawania' kolejnych typow zdarzen na filedescriptorach.
Dany proces: nie mam pojęcia, może netlink i taskstats. Dziecko: SIGCHLD. Usunięty plik: ionotify. Interfejs sieciowy: znów nie mam pojęcia, netlink (możesz sobie zajrzeć w źródła iproute2 — mi się nie chce)? Jeśli dobrze strzelam tym netlinkiem to wszystko powyższe reprezentowane jest przez fd. I może być użyte w epoll.
W przypadku kqueue jest to wszystko i niewiele więcej. Niestety jest to sztywny i kiepsko rozszerzalny interfejs.
@bies: Jest dokladnie na odwrot – dodanie kolejnego typu zdarzenia w kqueue(2) jest proste, bo jest do tego cala infrastruktura. W przypadku epoll() dodanie nowego typu zdarzenia wymaga dodania nowego syscalla, robiacego ze zdarzenia fd, dopisania wymaganych kawalkow do glibca, poczekania, az dystrybucje to wciagna itd.
No asem inżynierii oprogramowania to Ty nie jesteś. Ale z drugiej strony czy ja się spodziewałem, że jesteś? No nie.
Tłumaczę w miarę prosto: mamy dwóch programistów, Ziutek i Stefan. Ziutek napisał interfejs fubar a Stefan gumuś. Mamy teraz dwie programisterki: Zosia i Krysia. Zosia chciałaby użyć fubara a Krysia gumusia. Tylko, że Zosia sobie myśli: ,,Ziutek, nie daruję Ci tej nocy… Nie chcę Cię znać!''. Ale Zosia widzi że aby użyć fubara wystarczy, że utworzy obiekty fu i jej użytkownicy mogą użyć fubara z jej kodem bez ingerencji Ziutka (,,Ziutek, a całuj Ty mnie w nos!'').
Krysia tymczasem uśmiecha się do Stefana aby ten dostosował gumusia do Krysinych obiektów. Pytanie, czy uśmiecha się dość promiennie aby Stefan to zrobił? Tadadada!
I to jest właśnie różnica między elastycznym interfejsem a sztywnym. Jeśli osoby mojej małej opowieści wydają Ci się obce to podpowiem, że obiekty fu zwane są czasami fd (albo HANDLE jeśli popatrzeć przez okno
).
Inżynieria oprogramowania. W wydaniu brazylijskiej telenoweli. Paniał?
@bies: A teraz popatrz uwaznie: w przypadku Linuksa, zeby dodac nowy typ obiektu, trzeba dodac nowa funkcje systemowa, dodac kawalek kodu do glibca, i zaimplementowac obsluge. W przypadku FreeBSD i OSX, zeby dodac nowy typ obiektu, trzeba dodac jednego define'a (definiujacego typy obiektow) i zaimplementowac obsluge. Ponadto, w przypadku FreeBSD implementacja jest o tyle prostsza, ze jest do tego framework – a w przypadku Linuksa trzeba najpierw wykombinowac, jak by tu udawac na deskryptorze pliku cos, co nie ma z plikiem nic wspolnego.
I to jest wlasnie roznica miedzy interfejsem elastycznym i sztywnym – kqueue(2) jest elastyczne, bo pozwala na latwe dodawanie kolejnych typow zdarzen. W przypadku Linuksa brakuje takiego interfejsu, wiec dla kazdego kolejnego typu zdarzenia dodajesz nowy interfejs – eventfd, signalfd itd.
Poza tym zauwaz, ze kqueue(2) jest nadzbiorem epoll(). Wszystko, co mozna zrobic przy uzyciu epoll(), mozna zrobic przy uzyciu kqueue(2) – w szczegolnosci, moznaby dorobic, na wzor Linuksa, oddzielne interfejsy dla kazdego typu obiektu – ale nie ma to sensu, chyba ze w celu ulatwienia portowania oprogramowania z Linuksa. W szczegolnosci nie ma to sensu architektonicznego, bo deskryptory plikow z samej natury sluza do wejscia/wyjscia, a nie zdarzen.
Gościu, litości… Nie potrafię już prościej. Epoll rozszerza się nie dotykając implementacji epoll — wystarczy podać użytkownikowi fd (nie trzeba dodawać define'a i implementować obsługi). Rozszerzenie kqueue to zmiana implementacji kqueue. Weź pierwszą lepszą książkę o inżynierii oprogramowania (spokojnie, nie musi to od razu być Knuth) i poczytaj o interfejsach…
A do czego służy HANDLE? Zapominasz co jest plikiem w Uniksach. Pod tym względem Linux jest bardziej uniksowy niż fribzdi.
A w ogóle to marudzisz z tym glibcem strasznie. Popatrz na inotify, nie było kwestii ,,zróbmy tak aby zdarzenia inotify widoczne były w epoll''. Po prostu były widoczne od razu. Nie tylko w epoll, w select i poll takoż. Używasz fd i dostajesz obsługę zdarzeń za darmo. A interfejs w userspace i tak musi powstać bo sama sygnalizacja zdarzeń to nie wszystko — trzeba je jeszcze obsłużyć.
Tak czy inaczej kqueue to anty-inżynieria. To tak jak most po którym mogą jeździć tylko czołgi i samochody. I żeby przepuścić motocykle trzeba dla nich dobudować kolejny kawałek mostu.
@bies: Wiec twoim zdaniem istniejaca w Linuksie koniecznosc dodawania za kazdym razem kolejnego interfejsu (robiacego fd z czegostam) zamiast wykorzystywania elastycznego frameworka w rodzaju kqueue(2) dowodzi elastycznosci epoll() – w sensie, twierdzisz, ze epoll() jest elastyczne, bo nie da sie rozszerzyc jego funkcjonalnosci i trzeba dodawac za kazdym razem nowego syscalla. Nie no, spoko.
Moim zdaniem brakuje Ci roku studiów na specjalizacji inżynieria oprogramowania. Epoll odpowiada interfejsowi opartemu na klasie abstrakcyjnej i funkcjach wirtualnych. A kqueue wielkiemu switchowi. I twierdzę, że epoll/poll/select jest elastyczne bo nie trzeba rozszerzać jego funkcjonalności aby obsłużyć zdarzenie foo. EOD, nie mam zapędów pedagogicznych.
Oczywiście możesz płakać i jęczeć. Ale to nie zmieni faktu, że kqueue jest skopane. Jak już nie chcesz patrzeć na epoll (bo oczywista przewaga Linuksa nad fribzdi gryzie Cię w oczy — o czym wiadomo nie od dziś). To popatrz na WaitForMultipleObjects (mimo, że epoll to WFMO zrobione poprawnie) i porównaj obsługę HANDLE z kqueue. Porównaj też IOCP z kqueue — to będzie ciekawe przeżycie dla Ciebie — coś jak Task Scheluder z TBB + epoll. Czyli libdispatch zrobione poprawnie.
Albo zobacz jak to jest zrobione w Solku (/dev/poll). Jakoś tak się dziwnie składa, że wszystkie najważniejsze systemy serwerowe (Linux, Windows, Solaris, HP-UX, AIX, ś. p. Tru64) mają to rozwiązane podobnie. Tylko bsdziaże i jabłuszka mają cudaczne kqueue.
Mam dla Ciebie test: ja Cię nie przekonam, z resztą to nie jest mój cel. Ale pokaż powyższa dyskusję komuś z solidną wiedzą z inżynierii oprogramowania. Znasz jakiegoś ,,serwerowego dziadka'' któremu ufasz? Najlepiej piszącego pod Windows (aby nie był skrzywiony ku fribsdi lub Linuksowi). I zapytaj się go kto ma rację.
@bies: Z tego, co piszesz, wyraznie widac, ze jestes co prawda bardzo dumny z zaliczenia kolejnego semestru, ale nie masz bladego pojecia, jak dziala system operacyjny – ani jeden, ani drugi. Postaram sie wiec wytlumaczyc ci, skupiajac sie na roznicach miedzy kqueue(2) i epoll().
Przede wszystkim musisz sobie uswiadomic, ze kqueue(2) to nadzbior epoll(). Innymi slowy, epoll() rozni sie od kqueue glownie tym, ze nie potrafi operowac na niczym poza deskryptorami plikow. Wszystko, co da sie zrobic przy uzyciu epoll(), da sie zrobic uzywajac kqueue(2).
Efektem braku mozliwosci obslugi przez epoll() czegokolwiek poza filedeskryptorami jest koniecznosc dokladania kolejnych funkcji systemowych, ktore potrafia zrobic filedeskryptor z "czegos", gdzie "cos" moze byc na przyklad sygnalem. Dla kazdego rodzaju "czegos" musisz miec w Linuksowym kernelu oddzielna funkcje systemowa.
Twoje stwierdzenie, ze "epoll nie trzeba rozszerzac", jest, o dziwo, zgodne z prawda. Tak, epoll() nie da sie rozszerzyc, bo nie ma jak – z powodu ograniczen interfejsu nic poza filedeskryptorem mu nie przekazesz. Trzeba natomiast rozszerzac implementacje "pod spodem", zeby sygnalizowac na filedeskryptorach specyficzne dla typu obiektu zdarzenia.
Dokladnie tak samo dziala to w przypadku kqueue(2) – zmienia sie to "pod spodem", nie zmienia sie natomiast implementacja syscalla.
Nie wiem, jak wyglada implementacja w Linuksie, ale we FreeBSD poszczegolne typy zdarzen sa zrealizowane wlasnie jak funkcje wirtualne – eventy maja wskazniki na funkcje implementujace co trzeba. Zakladam, ze w Linuksie jest podobnie. Roznice sa dwie:
Pierwsza roznica to ta, ze w Linuksie interfejs jest nierozszerzalny – aby dodac nowy rodzaj zdarzen trzeba dodac nowy interfejs (w sensie, funkcje systemowa). Sam zreszta o tym wspomniales.
Druga roznica to ta, ze w Linuksie zdarzenia "udaje sie" na deskryptorach plikow. A to niesie ze soba pewne problemy – deskryptory plikow maja cechy, ktore nijak sie maja do zdarzen, na przyklad standard wymaga, aby byly alokowane po kolei. A to z kolei powoduje zmniejszenie skalowalnosci rozwiazania Linuksowego w porownaniu do pozostalych – bo okreslenie 'pierwszego wolnego' jest waskim gardlem.
W przypadku Windows tego problemu nie ma – Win32 nie stawia wymogow w rodzaju "pierwszy wolny"; poza tym caly podsystem byl tam od poczatku pod tym katem projektowany. W przypadku Solarisa /dev/epoll dodano pozno, i zrobiono to w taki sposob, aby jak najmniej namieszac w systemie. Pozniejsze rozwiazanie – I/O Completion Ports – ma bardzo blisko do kqueue(2) – deskryptor masz jeden, a potem dodajesz do niego kolejne zdarzenia, dokladnie jak w kqueue(2) i odwrotnie niz w epoll().
Podsumowujac – wrzucenie do jednego worka "udawanie" eventow i filedeskryptorow robi tylko Linux i Windows, przy czym Windows zostalo pod tym katem zaprojektowane od poczatku i w zwiazku z tym realizuje to w naturalny sposob, natomiast w Linuksie sprobowano to (nieudolnie, imho) nasladowac, z takim sobie skutkiem. Pozostale systemy – OSX, Solaris i FreeBSD – traktuja eventy jako eventy, nie mieszajac ich z deskryptorami plikow.
Mam dla ciebie test – pokaz powyzsza dyskusje komus z solidna wiedza o systemach operacyjnych. Moze byc Solaris, zeby nie byl skrzywiony ani ku FreeBSD, ani kolejnemu prywatnemu systemowi IBM-a. Nie powinien to byc nikt od Windows, bo chodzi o osobe, ktora wie, jak dzialaja _uniksy_. Najlepiej, zeby znal system od srodka, z punktu widzenia osoby rozwijajacej jej. I zapytaj go, kto ma racje. I nie przejmuj sie, jesli cie wysmieje – systemy operacyjne to ciezka sprawa. ;->
Brawo, jednak Cię przekonałem: interfejsu epoll nie ma potrzeby zmieniać — czyli jest kompletny. Oraz obsługuje każde fd, również takie opisujące zdarzenie o którym autor nie miał pojęcia że zaistnieje — czyli jest rozszerzalny.
To, że mogę zmienić interfejs i wtedy mostem przejedzie motocykl to żadna rozszerzalność (bo cały czas nie pojedzie rower, albo pojazd o którym jeszcze nie wiem, że będzie użyty).
Widzę, że coś zaczyna do Ciebie docierać choć miotasz się straszliwie.
Tylko jakim cudem postawiłeś Solarisa koło fribzdi… Przecież /dev/poll (nie /dev/epoll, sic!) to mechanizm bardzo podobny do epoll. Poza tym nie muszę rozszerzać interfejsu IOCP aby obsłużyć nowe zdarzenie. Wystarczy, że będzie jakieś HANDLE.
Cały czas nie rozumiesz dlaczego kqueue jest złym rozwiązaniem. Czy dodając zdarzenie na które czeka CompletionPort (czy też epoll, /dev/poll, poll, select, WFMO) muszę podawać jaki to jest typ zdarzenia. Nie! W kqueue — tak. I to jest programistyczny błąd: kqueue próbuje odpowiedzieć na pytanie ,,Jakiego typu jest zdarzenie które właśnie nastąpiło?''.
Gdy, tymczasem, cała reszta świata odpowiada na pytanie: ,,Czy nastąpiło zdarzenie?''. A jakie to jest zdarzenie — to już przynależy użytkownikowi który się o to zdarzenie pytał.
Widzę, że nie wiesz jak ważne jest postawienie właściwego pytania w inżynierii (nie tylko oprogramowania). Niestety nie wykpisz się zaliczaniem mi semestrów czy wyśmiewaniem mnie przez programistę uniksa — brakuje Ci podstawowej wiedzy. Nie nt. systemów operacyjnych, nie nt. programowania nawet. Na temat interfejsów.
Jeśli uważasz, że kqueue to interfejs ogólny to zaimplementuj za pomocą kqueue epoll (w userspace, bez orania jądra). A zauważyłeś czym się różni kqueue w NetBSD od tej z fribzdi?
@bies: Byloby latwiej, gdybysmy rozmawiali na temat kqueue(2), a nie twoich wyobrazen na temat kqueue(2). Dalsza rozmowa raczej nie ma sensu, ale odpowiem na jedno pytanie:
Tak, kqueue(2) to jest interfejs ogolny. Nie musze implementowac przy uzyciu kqueue(2) epoll(), poniewaz juz to jest – funkcjonalnosc epoll() jest podzbiorem funkcjonalnosci kqueue(2); podstawowa roznica polega na tym, ze epoll() potrafi czekac tylko na filedeskryptory, a kqueue(2) – nie.
Dobra, dobra. Rozumiem, że się wycofujesz rakiem. Ale powiedz mi, musisz podać na jaki typ zdarzenia czekasz w kqueue, czy nie?
@maciek: Inna sprawa, ze z GCD, poza oczywistym problemem politycznym, ktory mozna podsumowac jako 'GNU nienawidzi Apple', jest jeszcze problem techniczny – libdispatch bardzo intensywnie uzywa kqueue(2), ktore nie ma pelnowartosciowego odpowiednika w Linuksie. Wiec port na Linuksa wymagalby albo wywrocenia kernela do gory nogami w celu dodania odpowiedniej funkcjonalnosci, albo dodania masy hackow do libdispatch w celu obejscia tego braku funkcjonalnosci.