Czytałem więc wiele informacji na temat danych pomocniczych strumienia Unix, ale brakuje jednej rzeczy w całej dokumentacji, co powinno się stać, gdy nastąpi częściowy odczyt?
Załóżmy, że otrzymuję następujące komunikaty do 24-bajtowego bufora
msg1 [20 byes] (no ancillary data)
msg2 [7 bytes] (2 file descriptors)
msg3 [7 bytes] (1 file descriptor)
msg4 [10 bytes] (no ancillary data)
msg5 [7 bytes] (5 file descriptors)
Przy pierwszym wywołaniu recvmsg otrzymuję cały msg1 (i część msg2? Czy system operacyjny kiedykolwiek to zrobi?) Jeśli otrzymam część msg2, czy od razu otrzymam dane pomocnicze i muszę je zapisać do następnego odczytu kiedy wiem, co faktycznie mówi mi wiadomość, aby zrobić z danymi? Jeśli zwolnię 20 bajtów z msg1, a następnie ponownie wywołam recvmsg, czy kiedykolwiek dostarczy on msg3 i msg4 w tym samym czasie? Czy dane pomocnicze z msg3 i msg4 są konkatenowane w strukturze komunikatu kontrolnego?
Podczas gdybym mógł pisać programy testowe, aby eksperymentalnie to odkryć, szukam dokumentacji na temat tego, jak zachowują się dane pomocnicze w kontekście transmisji strumieniowej. Dziwne, że nie mogę znaleźć na nim niczego oficjalnego.
Dodam tutaj moje wyniki eksperymentalne, które otrzymałem z tego programu testowego:
https://github.com/nrdvana/daemonproxy/blob/master/src/ancillary_test.c
Linux 3.2.59, 3.17.6
Wygląda na to, że Linux dołącza fragmenty komunikatów pomocniczych na końcu innych komunikatów, o ile nie będzie potrzeby dostarczenia wcześniejszej dodatkowej zawartości podczas tego wezwania do recvmsg. Po dostarczeniu danych pomocniczych jednej wiadomości zwróci ona krótki odczyt, a nie rozpocznie następnej wiadomości danych pomocniczych. Tak więc w powyższym przykładzie otrzymane odczyty to:
recv1: [24 bytes] (msg1 + partial msg2 with msg2's 2 file descriptors)
recv2: [10 bytes] (remainder of msg2 + msg3 with msg3's 1 file descriptor)
recv3: [17 bytes] (msg4 + msg5 with msg5's 5 file descriptors)
recv4: [0 bytes]
BSD 4.4, 10.0
BSD zapewnia więcej wyrównania niż Linux i daje krótki odczyt bezpośrednio przed rozpoczęciem wiadomości z danymi pomocniczymi. Ale z radością dołączy wiadomość o charakterze pomocniczym na końcu komunikatu o charakterze pomocniczym. Tak więc w przypadku BSD wygląda na to, że jeśli twój bufor jest większy niż komunikat zawierający informacje pomocnicze, możesz uzyskać zachowanie podobne do pakietu. Otrzymane odczyty to:
recv1: [20 bytes] (msg1)
recv2: [7 bytes] (msg2, with msg2's 2 file descriptors)
recv3: [17 bytes] (msg3, and msg4, with msg3's 1 file descriptor)
recv4: [7 bytes] (msg5 with 5 file descriptors)
recv5: [0 bytes]
DO ZROBIENIA:
Chciałbym nadal wiedzieć, jak to się dzieje na starszych systemach Linux, iOS, Solaris itp. I jak można się spodziewać w przyszłości.
źródło
Odpowiedzi:
W pozostałej części pytania sprawy stają się nieco owłosione.
Nowoczesne gniazda BSD dokładnie pasują do tego wyciągu. Nie jest to zaskakujące :-).
Pamiętaj, że standard POSIX został napisany po UNIX i po podziale, takim jak BSD vs. System V. Jednym z głównych celów było zrozumienie istniejącego zakresu zachowań i zapobieganie jeszcze większemu podziałowi w istniejących funkcjach.
Linux został zaimplementowany bez odniesienia do kodu BSD. Wygląda na to, że zachowuje się tutaj inaczej.
Jeśli czytam cię prawidłowo, to brzmi jak Linux jest dodatkowo łączenie „segmentów”, kiedy nowy segment ma zawierać dane pomocnicze, ale poprzedni segment nie.
Twój punkt, w którym „Linux dołącza części komunikatów pomocniczych na końcu innych komunikatów, o ile nie było potrzeby dostarczenia wcześniejszej dodatkowej zawartości podczas tego wezwania do recvmsg”, nie wydaje się być całkowicie wyjaśniony przez standard. Jednym z możliwych wyjaśnień byłby warunek wyścigu. Jeśli przeczytasz część „segmentu”, otrzymasz dane pomocnicze. Być może Linux zinterpretował to jako oznaczające, że pozostała część segmentu nie liczy się już jako zawierająca dane pomocnicze! Tak więc po otrzymaniu nowego segmentu jest on scalany - zgodnie ze standardem lub zgodnie z różnicą 1 powyżej.
Jeśli chcesz napisać maksymalnie przenośny program, powinieneś całkowicie unikać tego obszaru. Podczas korzystania z danych pomocniczych znacznie częściej używa się gniazd datagramów . Jeśli chcesz pracować na wszystkich dziwnych platformach, które technicznie dążą do zapewnienia czegoś w rodzaju POSIX, twoje pytanie wydaje się zapuszczać w ciemny i niesprawdzony zakątek.
Można argumentować, że Linux nadal przestrzega kilku istotnych zasad:
Nie jestem jednak przekonany, że zachowanie Linuksa jest szczególnie przydatne , gdy porównasz je z zachowaniem BSD. Wygląda na to, że opisany program musiałby dodać obejście specyficzne dla systemu Linux. I nie znam uzasadnienia, dlaczego Linux miałby się tego spodziewać.
Pisanie kodu jądra Linuksa mogło wydawać się rozsądne, ale bez testowania lub wykonywania przez jakikolwiek program.
Lub może być wykonywany przez jakiś kod programu, który w większości działa w tym podzbiorze, ale w zasadzie może mieć „błędy” lub warunki wyścigu.
Jeśli nie możesz zrozumieć zachowania Linuksa i jego zamierzonego użycia, myślę, że przemawia to za potraktowaniem tego jako „ciemnego, niesprawdzonego kąta” w Linuksie.
źródło