Mam na myśli standardowe wywołania API C systemu wyboru i sondowania POSIX .
Mam na myśli standardowe wywołania API C systemu wyboru i sondowania POSIX .
Odpowiedzi:
Myślę, że to odpowiada na twoje pytanie:
Od Richarda Stevensa (rstevens@noao.edu):
Podstawowa różnica polega na tym, że fd_set funkcji select () jest maską bitową i dlatego ma pewien stały rozmiar. Byłoby możliwe, aby jądro nie ograniczało tego rozmiaru podczas kompilacji jądra, pozwalając aplikacji na zdefiniowanie FD_SETSIZE do tego, czego chce (jak sugerują dzisiejsze komentarze w nagłówku systemu), ale wymaga to więcej pracy. Jądro 4.4BSD i funkcja biblioteczna Solaris mają to ograniczenie. Ale widzę, że BSD / OS 2.1 został teraz zakodowany, aby uniknąć tego ograniczenia, więc jest to wykonalne, tylko mała kwestia programowania. :-) Ktoś powinien zgłosić błąd w Solarisie i sprawdzić, czy kiedykolwiek zostanie naprawiony.
Jednak w przypadku funkcji poll () użytkownik musi przydzielić tablicę struktur pollfd i przekazać liczbę wpisów w tej tablicy, więc nie ma podstawowego ograniczenia. Jak zauważa Casper, mniej systemów ma funkcję poll () niż select, więc ta ostatnia jest bardziej przenośna. Ponadto w oryginalnych implementacjach (SVR3) nie można było ustawić deskryptora na -1, aby nakazać jądru ignorowanie wpisu w strukturze pollfd, co utrudniało usuwanie wpisów z tablicy; SVR4 omija ten problem. Osobiście zawsze używam select () i rzadko poll (), ponieważ przenoszę swój kod również do środowisk BSD. Ktoś mógłby napisać implementację poll (), która używa funkcji select () dla tych środowisk, ale nigdy takiej nie widziałem. Zarówno select (), jak i poll () są standaryzowane przez POSIX 1003.1g.
Adres e-mail, o którym mowa powyżej, ma co najmniej rok 2001; poll()
komenda jest teraz (2017) obsługiwany we wszystkich nowoczesnych systemów operacyjnych - w tym BSD. W rzeczywistości niektórzy uważają, że select()
powinno to zostać wycofane . Pomijając opinie, problemy z przenośnością poll()
nie dotyczą już nowoczesnych systemów. Co więcej, epoll()
od tego czasu został opracowany (możesz przeczytać stronę podręcznika ) i nadal zyskuje na popularności.
W przypadku współczesnego programowania prawdopodobnie nie chcesz go używać select()
, chociaż nie ma w tym nic złego. poll()
i jest to bardziej nowoczesna ewolucja epoll()
, zapewnia te same funkcje (i więcej), co select()
bez cierpienia z powodu ograniczeń.
select
czy poll
:(
select()
Wezwanie musi utworzyć trzy bitmasks do znaku, który gniazd i deskryptory plików, które chcesz oglądać do czytania, pisania i błędy, a następnie oznaczeniami system operacyjny, który z nich w rzeczywistości miały jakąś aktywność; poll()
czy utworzyłeś listę identyfikatorów deskryptorów, a system operacyjny oznacza każdy z nich rodzajem zdarzenia, które miało miejsce.
select()
Metoda jest raczej niezgrabne i nieefektywne.
Zazwyczaj proces ma do dyspozycji ponad tysiąc potencjalnych deskryptorów plików. Jeśli długotrwały proces ma otwartych tylko kilka deskryptorów, ale przynajmniej jednemu z nich przypisano wysoką liczbę, wówczas przekazana maska bitowa select()
musi być wystarczająco duża, aby pomieścić ten najwyższy deskryptor - więc całe zakresy setek bitów będą nie przejmuj się tym, że system operacyjny musi przechodzić przez pętlę przy każdym select()
wywołaniu tylko po to, aby odkryć, że nie są one ustawione.
Po select()
powrocie obiekt wywołujący musi wykonać pętlę po wszystkich trzech maskach bitowych, aby określić, jakie zdarzenia miały miejsce. W bardzo wielu typowych aplikacjach tylko jeden lub dwa deskryptory plików otrzymają nowy ruch w dowolnym momencie, jednak wszystkie trzy maski bitowe muszą zostać przeczytane do końca, aby odkryć, które to są deskryptory.
Ponieważ system operacyjny sygnalizuje aktywność, przepisując maski bitowe, są one zniszczone i nie są już oznaczone listą deskryptorów plików, których chcesz słuchać. Musisz albo odbudować całą maskę bitową z innej listy, którą trzymasz w pamięci, albo musisz zachować zduplikowaną kopię każdej maski bitowej i memcpy()
bloku danych na zrujnowanych maskach bitowych po każdym select()
wywołaniu.
Tak więc poll()
podejście to działa znacznie lepiej, ponieważ możesz nadal używać tej samej struktury danych.
W rzeczywistości poll()
zainspirował kolejny mechanizm w nowoczesnych jądrach Linuksa: epoll()
który jeszcze bardziej ulepsza mechanizm, umożliwiając kolejny skok skalowalności, ponieważ dzisiejsze serwery często chcą obsługiwać dziesiątki tysięcy połączeń naraz. To jest dobre wprowadzenie do tego wysiłku:
http://scotdoyle.com/python-epoll-howto.html
Chociaż ten link ma kilka ładnych wykresów pokazujących korzyści epoll()
(zauważysz, że select()
w tym momencie jest uważany za tak nieefektywny i staromodny, że nawet nie ma linii na tych wykresach!):
http://lse.sourceforge.net/epoll/index.html
Aktualizacja: Oto kolejne pytanie dotyczące przepełnienia stosu, którego odpowiedź zawiera jeszcze więcej szczegółów na temat różnic:
Ostrzeżenia dotyczące reaktorów typu select / poll w porównaniu z reaktorami epoll w Twisted
Oba są powolne iw większości takie same , ale różnią się rozmiarem i pewnymi funkcjami!
Kiedy piszesz iterator, za select
każdym razem musisz skopiować zestaw ! Chociaż poll
rozwiązał ten problem, aby mieć piękny kod. Inną różnicą jest to, że poll
domyślnie obsługuje więcej niż 1024 deskryptory plików (FD). poll
może obsługiwać różne zdarzenia, aby uczynić program bardziej czytelnym, zamiast posiadania wielu zmiennych do obsługi tego rodzaju pracy. Operacje w poll
i select
są liniowe i powolne z powodu dużej liczby sprawdzeń.