Итак, я прочитал много информации о вспомогательных данных unix-stream, но во всей документации отсутствует одна вещь: что должно происходить при частичном чтении?
Предположим, я получаю следующие сообщения в 24-байтовый буфер
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)
При первом вызове recvmsg я получаю все сообщения msg1 (и часть сообщения msg2? Будет ли ОС когда-либо это делать?) Если я получу часть сообщения msg2, получу ли я вспомогательные данные сразу же, и мне нужно сохранить их для следующего чтения? когда я узнаю, что сообщение на самом деле говорит мне делать с данными? Если я освобожу 20 байтов от msg1 и затем снова вызываю recvmsg, будут ли они одновременно доставлять msg3 и msg4? Вспомогательные данные из msg3 и msg4 объединяются в структуре управляющего сообщения?
Хотя я мог бы написать тестовые программы для экспериментального выяснения этого, я ищу документацию о том, как вспомогательные данные ведут себя в контексте потоковой передачи. Кажется странным, что я не могу найти ничего официального на этом.
Я собираюсь добавить свои экспериментальные результаты, которые я получил из этой тестовой программы:
https://github.com/nrdvana/daemonproxy/blob/master/src/ancillary_test.c
Linux 3.2.59, 3.17.6
Похоже, что Linux будет добавлять части вспомогательных сообщений в конец других сообщений, если во время этого вызова recvmsg не нужно было доставлять какую-либо предварительную полезную нагрузку. Как только вспомогательные данные одного сообщения доставляются, он вернет короткое чтение, а не запустит следующее сообщение вспомогательных данных. Итак, в приведенном выше примере чтения я получаю:
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 обеспечивает больше выравнивания, чем Linux, и дает краткое чтение непосредственно перед началом сообщения с вспомогательными данными. Но он с радостью добавит несвязанное сообщение к концу вспомогательного сообщения. Таким образом, для BSD, похоже, что если ваш буфер больше, чем вспомогательное сообщение, вы получаете почти пакетное поведение. Чтения, которые я получаю:
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]
СДЕЛАТЬ:
Хотелось бы еще узнать, как это происходит на старых Linux, iOS, Solaris и т. Д., И как это может произойти в будущем.
источник
Ответы:
Для остальной части вашего вопроса вещи становятся немного волосатыми.
Поэтому современные разъемы BSD точно соответствуют этому экстракту. Это не удивительно :-).
Помните, стандарт POSIX был написан после UNIX и после разделений, таких как BSD против System V. Одна из основных целей состояла в том, чтобы помочь понять существующий диапазон поведения и предотвратить еще больше разделений в существующих функциях.
Linux был реализован без ссылки на код BSD. Кажется, здесь ведут себя по-другому.
Если я правильно читаю вас, это звучит как Linux дополнительно объединение «сегментов» , когда новый сегмент делает включать вспомогательные данные, но предыдущий сегмент не делает.
Ваше утверждение о том, что «Linux будет добавлять части вспомогательных сообщений в конец других сообщений, если во время этого вызова recvmsg не требовалось доставлять дополнительную вспомогательную нагрузку», кажется, полностью не объясняется стандартом. Одним из возможных объяснений может быть состояние гонки. Если вы прочитаете часть «сегмента», вы получите вспомогательные данные. Возможно, Linux интерпретировал это как означающее, что оставшаяся часть сегмента больше не считается включением вспомогательных данных! Поэтому при получении нового сегмента он объединяется - либо в соответствии со стандартом, либо согласно разнице 1 выше.
Если вы хотите написать максимально переносимую программу, вам следует вообще избегать этой области. При использовании вспомогательных данных гораздо чаще используются гнезда дейтаграмм . Если вы хотите работать на всех странных платформах, которые технически стремятся предоставить что-то, в основном подобное POSIX, ваш вопрос, похоже, уходит в темный и непроверенный угол.
Можно утверждать, что Linux по-прежнему следует нескольким важным принципам:
Однако я не уверен, что поведение Linux особенно полезно , если сравнивать его с поведением BSD. Кажется, что программа, которую вы описываете, должна добавить специфический для Linux обходной путь. И я не знаю оправдания того, почему Linux ожидает, что вы это сделаете.
Это могло бы показаться разумным при написании кода ядра Linux, но без какой-либо проверки или выполнения какой-либо программой.
Или это может быть реализовано некоторым программным кодом, который в основном работает в этом подмножестве, но в принципе может иметь «ошибки» в крайнем случае или условия гонки.
Если вы не можете разобраться в поведении Linux и его предполагаемом использовании, я думаю, что это приводит к трактовке этого «темного, непроверенного угла» в Linux.
источник