Thread View: pl.comp.os.linux.programowanie
7 messages
7 total messages
Started by heby
Wed, 04 Nov 2020 09:31
::read i ::poll
Author: heby
Date: Wed, 04 Nov 2020 09:31
Date: Wed, 04 Nov 2020 09:31
26 lines
2328 bytes
2328 bytes
Cześć. Czytam z rurki za pomocą ::read. Blokująco, bo na tym polega synchronizacja w tym konkretnym przypadku. Ale muszę raz na jakiś czas stwierdzić, czy nie czas awaryjnie się składać, obejrzeć jakas flagę, zawołać jakiś watchdog itd. ::read blokującego nie da się przerwać. Co prawda jest niby EINTR ale ja czekam w wielu wątkach na wielu rurach i musze precyzyjnie przerwać bardzo konkretny ::read, ten, a nie inny. Rozwiązanie okazało się proste: użyć ::poll. Czeka on aż pojawią się jakieś dane do odczytu, ale ma też timeout po którym zwraca mi sterowanie i to załatwia temat przerywalnosci odczytu. I wszystki super działa, tylko jest ... znacznie wolniejsze. Wywołanie ::read jest szybsze niż pary ::poll->::read. No i co z tym teraz zrobić? Jest jakaś inna metoda na posiadanie blokującego ::read ale jednocześnie możliwosci przerwania tego read na żądanie? PS. Mogę zamknąc deskryptor pliku i wtedy ::read wylautuje z błedem. Ale to jest nieeleganckie, ponadto nie dośc że powoduje race-conditions, to jeszcze jest destrukcyjne, tzn komunikacja jest już niemożliwa do wznowienia. A ja chce przerwać read, coś sprawdzić i wrócić do czekania na dane.
Re: ::read i ::poll
Author: michal.lyszczek
Date: Thu, 05 Nov 2020 10:45
Date: Thu, 05 Nov 2020 10:45
22 lines
2024 bytes
2024 bytes
On 2020-11-04 10:31:56, heby wrote: > Rozwiązanie okazało się proste: użyć ::poll. Czeka on aż pojawią się jakieś > dane do odczytu, ale ma też timeout po którym zwraca mi sterowanie i to > załatwia temat przerywalnosci odczytu. > > I wszystki super działa, tylko jest ... znacznie wolniejsze. Wywołanie > ::read jest szybsze niż pary ::poll->::read. No i co z tym teraz zrobić? > Jest jakaś inna metoda na posiadanie blokującego ::read ale jednocześnie > możliwosci przerwania tego read na żądanie? Najprościej będzie wysłać sygnał do konkretnego wątku przy pomocy pthread_kill(3). Jak kod ma być przenośny między unixami, należy pamiętać by wyłączyć SA_RESTART na systemach z rodziny BSD przy pomocy siginterrupt(3). -- .-----------------.-------------------.---------------------.------------------. | Michal Lyszczek | Embedded C, Linux | Company Address | .-. open source | | +48 727 564 419 | Software Engineer | Leszczynskiego 4/29 | oo| supporter | | https://bofc.pl `----.--------------: 50-078 Wroclaw, Pol | /`'\ & | | GPG FF1EBFE7E3A974B1 | Bits of Code | NIP: 813 349 58 78 |(\_;/) programer | `----------------------^--------------^---------------------^------------------'
Re: ::read i ::poll
Author: heby
Date: Thu, 05 Nov 2020 11:57
Date: Thu, 05 Nov 2020 11:57
23 lines
1932 bytes
1932 bytes
On 05/11/2020 11:45, michal.lyszczek@bofc.pl wrote:
> Najprościej będzie wysłać sygnał do konkretnego wątku przy pomocy
> pthread_kill(3).
Co z taką sytuacją:
{
...
std::mutex::scoped_lock lock(mutex);
...
::read(...)
}
Zakładając że zainstaluje globalny handler sygnałów (co już jest błędem
projektowym, może nie tylko ten wątek go potrzebuje), czy mogę wsyłać
coś co spowoduje że ::read wyskoczy z EINTR?
> Jak kod ma być przenośny między unixami, należy
> pamiętać by wyłączyć SA_RESTART na systemach z rodziny BSD przy
> pomocy siginterrupt(3).
No i tu właśnie jest problem. To ogromna apliakcja. Z powodu kilku
::read mam manipulować krytyczm, globlanym stanem aplikacji? Brzmi jak 1
z kolokwium z inżynierii programowania.
Re: ::read i ::poll
Author: michal.lyszczek
Date: Thu, 05 Nov 2020 12:43
Date: Thu, 05 Nov 2020 12:43
75 lines
4126 bytes
4126 bytes
On 2020-11-05 12:57:04, heby wrote:
> On 05/11/2020 11:45, michal.lyszczek@bofc.pl wrote:
> > Najprościej będzie wysłać sygnał do konkretnego wątku przy pomocy
> > pthread_kill(3).
>
> Co z taką sytuacją:
> {
> ...
> std::mutex::scoped_lock lock(mutex);
> ...
> ::read(...)
> }
Nic, w takim przypadku nie pomoże pthread_kill(3) tylko select/poll/epoll.
>
> Zakładając że zainstaluje globalny handler sygnałów (co już jest błędem
> projektowym, może nie tylko ten wątek go potrzebuje), czy mogę wsyłać coś co
> spowoduje że ::read wyskoczy z EINTR?
Globalny handler sygnałów nie jest błędem projektowym, błędne użycie go
jest błędem projektowym. Signal handler, którego jedyne zadanie to przerwać
syscall, może mieć jedynie 'return;' w body i nie musi operować na żadnych
danych. Każdy sygnał przerywa syscall, możesz wyemitować np. SIGUSR1. Ale
jeżeli kilka wątków czyta ci jeden deskryptor plików to musiałbyś się bawić
w zmienne w stylu "thread_id_in_read", i na tej podstawie słać sygnały - ale
to już zabawa w synchronizację.
read(2) nie jest przystosowany do wielowątkowego obsługiwania, lepiej byłoby
aby 1 wątek robił read(2) na fd, jakoś to wstępnie procesował, po czym
wrzucał na jakąś synchronizowaną kolejkę, która może już być odczytywana
przez wiele wątków.
t1:
while (1) {
struct data_frame frame;
read_until_full_frame_is_received(&frame);
queue_push(&frame);
}
t2, t3, t4...:
while (1) {
struct data_frame frame;
queue_pop(&frame);
process_frame(&frame);
}
W takim przypadku to nawet nie powinno być potrzeby bawić się w EINTR. Nie
znam konkretnego użycia więc też ciężko się odnieść do tego trochę.
>
> > Jak kod ma być przenośny między unixami, należy
> > pamiętać by wyłączyć SA_RESTART na systemach z rodziny BSD przy
> > pomocy siginterrupt(3).
>
> No i tu właśnie jest problem. To ogromna apliakcja. Z powodu kilku ::read
> mam manipulować krytyczm, globlanym stanem aplikacji? Brzmi jak 1 z
> kolokwium z inżynierii programowania.
Jak 1 z kolokwium to brzmi "To ogromna aplikacja" ;-) Dodatkowo, zmierzyłeś
że problem z wydajnością jest spowodowany przez select/poll? Jak masz tam
raptem kilka deskryptorów, to ciężko mi uwierzyć żeby to miało taki narzut
na wydajność i problem jest zapewne gdzie indziej. To, albo optymalizujesz
program na siłę.
Jak bardzo chcesz mieć nieblokujący read(2) oraz nie używać polla to możesz
zawsze ustawić przy pomocy fcntl(2) ustawić flagę O_NONBLOCK, i przy każdym
read(2) gdy nie będzie danych dostaniesz zwrotkę -1 oraz errno na EAGAIN.
--
.-----------------.-------------------.---------------------.------------------.
| Michal Lyszczek | Embedded C, Linux | Company Address | .-. open source |
| +48 727 564 419 | Software Engineer | Leszczynskiego 4/29 | oo| supporter |
| https://bofc.pl `----.--------------: 50-078 Wroclaw, Pol | /`'\ & |
| GPG FF1EBFE7E3A974B1 | Bits of Code | NIP: 813 349 58 78 |(\_;/) programer |
`----------------------^--------------^---------------------^------------------'
Re: ::read i ::poll
Author: heby
Date: Thu, 05 Nov 2020 14:26
Date: Thu, 05 Nov 2020 14:26
32 lines
2804 bytes
2804 bytes
On 05/11/2020 13:43, michal.lyszczek@bofc.pl wrote: > Jak 1 z kolokwium to brzmi "To ogromna aplikacja" ;-) To raczej problem z okolic "w ksiąze do programowani jest taki przykłąd na 3 linijki i działa", a potem masz bazę kodu na ~500MB (nie przesadzam) i zastanawiasz się jaki idiota wymyślił globalny handler który ma obsługiwać setki równoległych i kompletnie niezależnych części tej aplikacji. > Dodatkowo, zmierzyłeś > że problem z wydajnością jest spowodowany przez select/poll? Apliakcja wykonuje miliony ::read( *foo, 4 );. Ogólnie wykonuje bardzo dużo małych read na namedpipe, bezustannie komunikujac się z inną apliakcją (inny proces). Przypuszczam że każdy ::read to switch kontekstu do kernela, albo przynajmniej jakieś cieżkie operacje na granicy kernel/user space. > Jak masz tam > raptem kilka deskryptorów, to ciężko mi uwierzyć żeby to miało taki narzut > na wydajność i problem jest zapewne gdzie indziej. To, albo optymalizujesz > program na siłę. Nie, zostało to dokładnie rozpracowane. ::read jest znaczaco szybszy niż ::poll->::read. Nawet w syntetycznym przykładzie da się to zobaczyć. > Jak bardzo chcesz mieć nieblokujący read(2) oraz nie używać polla to możesz > zawsze ustawić przy pomocy fcntl(2) ustawić flagę O_NONBLOCK, i przy każdym > read(2) gdy nie będzie danych dostaniesz zwrotkę -1 oraz errno na EAGAIN. Jesli teraz zawolam ::read ponownie i tak w kółko to właśnie przerobiłem komputer na grzejnik. A jak wstawie usleep... no własnie...
Re: ::read i ::poll
Author: michal.lyszczek
Date: Thu, 05 Nov 2020 15:01
Date: Thu, 05 Nov 2020 15:01
29 lines
2611 bytes
2611 bytes
On 2020-11-05 15:26:23, heby wrote: > On 05/11/2020 13:43, michal.lyszczek@bofc.pl wrote: > > Dodatkowo, zmierzyłeś > > że problem z wydajnością jest spowodowany przez select/poll? > > Apliakcja wykonuje miliony ::read( *foo, 4 );. Ogólnie wykonuje bardzo dużo > małych read na namedpipe, bezustannie komunikujac się z inną apliakcją (inny > proces). Przypuszczam że każdy ::read to switch kontekstu do kernela, albo > przynajmniej jakieś cieżkie operacje na granicy kernel/user space. I to jest twój główny problem. Wołanie ciągle read(2) z tak małym parametrem count rozwala ci całkowicie wydajność. Jak sam piszesz, każdy read(2) to przejście do kernela, a to jest bardzo droga operacja, zwłaszcza jak robisz "miliony" tego. Moja propozycja rozwiązania jak wcześniej. Niech 1 wątek czyta ciągle w pętli deskryptor, z dużym rozmiarem (jak 10kB), jak będzie mniej danych niż 10kB to read(2) wyjdzie i zwróci liczbę odczytanych bajtów. Te dane wrzucaj na kolejkę synchronizowaną i niech sobie reszta wątków czyta to nawet i po 1 bajcie. Wołanie funkcji jest kilka rzędów wielkości szybsze niż wołanie syscalla. -- .-----------------.-------------------.---------------------.------------------. | Michal Lyszczek | Embedded C, Linux | Company Address | .-. open source | | +48 727 564 419 | Software Engineer | Leszczynskiego 4/29 | oo| supporter | | https://bofc.pl `----.--------------: 50-078 Wroclaw, Pol | /`'\ & | | GPG FF1EBFE7E3A974B1 | Bits of Code | NIP: 813 349 58 78 |(\_;/) programer | `----------------------^--------------^---------------------^------------------'
Re: ::read i ::poll
Author: heby
Date: Thu, 05 Nov 2020 18:16
Date: Thu, 05 Nov 2020 18:16
26 lines
2561 bytes
2561 bytes
On 05/11/2020 16:01, michal.lyszczek@bofc.pl wrote: > Moja propozycja rozwiązania jak wcześniej. Niech 1 wątek czyta ciągle > w pętli deskryptor Wtedy tracę coś co nazywa się synchronizacją. Konkretnie: mam przeczytać 3 bajty, stwierdzić że to oznacza "skasuj plik", podpowiedzeć "ack", druga strona utworzy nowy itd itp. Bezustannie są wymieniane polecenia pomiędzy dwoma procesami "zrób to i to","gotowe","zrób coś innego","a ty zrób to" itd. Mam pewna przestrzeń na grupowanie tych read/write, ale to mi nijak nie pomaga w poprawnym, przerywalnym read, bo problem z ::poll dalej będzie istniał, tyle że zredukuje jego siłę spowolnienia. Na razie wydaje mi się że nie da się tego zrobić dobrze, po prostu konepcja posixa z sygnałami jest gówno warta do większych zastosowań, ::pool+::read są drogie, innych rozwiązań nie ma. > Wołanie funkcji jest kilka rzędów wielkości > szybsze niż wołanie syscalla. Wszystko jest oczywiste, niesety messages latające po rurach mają na wyższym proziomie abstrakcji znaczenie synchronizujące i nie da się tego tak trywialnie z mienić.
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads