🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: pl.comp.os.linux.programowanie
7 messages
7 total messages Started by heby Wed, 04 Nov 2020 09:31
::read i ::poll
#72991
Author: heby
Date: Wed, 04 Nov 2020 09:31
26 lines
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
#72992
Author: michal.lyszczek
Date: Thu, 05 Nov 2020 10:45
22 lines
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
#72993
Author: heby
Date: Thu, 05 Nov 2020 11:57
23 lines
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
#72994
Author: michal.lyszczek
Date: Thu, 05 Nov 2020 12:43
75 lines
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
#72995
Author: heby
Date: Thu, 05 Nov 2020 14:26
32 lines
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
#72996
Author: michal.lyszczek
Date: Thu, 05 Nov 2020 15:01
29 lines
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
#72997
Author: heby
Date: Thu, 05 Nov 2020 18:16
26 lines
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