Частичная проблема чтения / записи в sendmsg/recvmsg
Я пишу программу, которая передает файловые дескрипторы между двумя процессами, используя sendmsg
а также recvmsg
через доменный сокет. Для отправки дескриптора файла дополнительные данные включены в msghdr.msg_iov
а также msghdr.msg_iolen
, Однако мне сообщили, что похоже на нормальное read
а также write
системный вызов, sendmsg
а также recvmsg
также есть частичная проблема чтения / записи. В этом случае будут ли данные во вспомогательном поле автоматически дублироваться для каждой частичной информации? Я спрашиваю об этом, потому что моя реализация требует неблокирующего механизма. Позвольте мне использовать следующий пример, чтобы развить его немного подробнее
Отправитель: отправить msghdr
данные, которые содержат fd
во вспомогательном поле и K
байты в msg_iov
Получатель: (1) частичное чтение, K1
байт (2) частичное чтение, K-K1
байтов
Теперь, как в приведенном выше примере, я должен обработать данные после шага (2), когда все данные поступят. В этом случае я могу все еще правильно извлечь fd
из вспомогательного поля? Или он появляется только при первом частичном чтении?
1 ответ
Быстро изучив исходный код ядра (linux, но см. Ниже), я считаю, что вам нужно убедиться, что вспомогательные данные отправляются только один раз. То есть в неблокирующем режиме, если в приемном сокете нет места, вы вернетесь EAGAIN
/EWOULDBLOCK
и ни данные, ни вспомогательные данные не будут отправлены. Но если на принимающей стороне есть место, то начальная часть данных будет отправлена, и вспомогательные данные также будут отправлены. Затем вы получите счетчик байтов возврата, указывающий на частичную отправку, но вспомогательные данные будут отправлены.
Вы должны знать об этом, когда вы пытаетесь отправить оставшуюся часть вашего сообщения, потому что ядро не сохраняет памяти, в которой вы ранее отправляли частичный буфер, с которым последующий буфер является логически смежным (на самом деле это не так, как вы могли бы - вы может посылать совершенно разные данные для всего, что он знает). Поэтому, если вы просто предоставите те же самые вспомогательные данные для последующих частей буфера, я считаю, что ядро с радостью снова доставит вспомогательные данные с вашими последующими частями буфера. Это может привести к появлению дубликатов файловых дескрипторов на стороне получателя (которые вы, вероятно, не захотите закрывать, поскольку не ожидаете их) - если вы не избегаете этого.
Теперь, если вы находитесь в режиме блокировки на отправляющей стороне, и передача разбивается на несколько частей, вспомогательные данные будут отправляться только один раз - с первой частью буфера, поскольку отправка всего буфера остается под контролем ядра.
Поэтому на принимающей стороне вам необходимо знать, что вспомогательные данные сопровождают первый фрагмент полученных данных, если вы не получили все логическое сообщение.
Я полагаю, что это поведение согласуется с тем, о котором сообщается в справке stackexchange, предоставленной @ Klas-Lindbäck ( https://unix.stackexchange.com/questions/185011/what-happens-with-unix-stream-ancillary-data-on-partial-reads). (Этот вопрос не касался неблокирующего режима.)
Этот ответ специфичен для Linux. Поэтому вполне возможно, что результаты будут немного отличаться на других ОС, хотя мне трудно понять, насколько они могут значительно отличаться и при этом поддерживать нормальную семантику. Ядро не может разумно хранить память о том, что было отправлено ранее, и sendmsg
прототип не позволяет перезаписать пользователя msghdr
чтобы отразить, что msg_control
часть уже отправлена.