структура sockaddr_un: правильная инициализация

Пару дней назад я столкнулся со странной ошибкой, связанной с struct sockaddr_un. Подробнее об этом можно прочитать здесь.

Через некоторое время я также нашел решение этой ошибки, которое заключалось в том, что memset устанавливал всю переменную целиком. Теперь мой вопрос, как уже задано в одном посте на той стороне, почему?

Почему мне нужно установить всю локально, а не динамически выделяемую переменную в 0, даже если я установил для каждого члена допустимое и правильное значение?

В других структурах (например, struct sockaddr_in) это переменная, которая используется в качестве заполнения, чтобы внутренне выглядеть так же, как struct sockaddr. >. Вы должны заполнить (или, может быть, не должны? Пожалуйста, поправьте меня) эту переменную с 0, чтобы программа работала правильно в любое время.

Кроме того, где я могу просмотреть исходный код, например, connect()? Может ли быть так, что функция connect() реализована неаккуратно? Или я упускаю некоторые основы?

Заранее спасибо.


person xQuare    schedule 04.08.2012    source источник
comment
connect() — это системный вызов. Он реализован в ядре.   -  person Zaffy    schedule 04.08.2012
comment
Может ли кто-нибудь предложить какие-нибудь онлайн-интерфейсы для просмотра исходного кода ядра? Например, я вхожу в функцию, а она мне показывает исходный код? Или что-то типа того...   -  person xQuare    schedule 04.08.2012
comment
На какой системе вы работаете?   -  person jxh    schedule 04.08.2012
comment
Нестандартное сжатие Debian.   -  person xQuare    schedule 04.08.2012
comment
Вы используете абстрактное пространство имен?   -  person jxh    schedule 04.08.2012
comment
Я использую локальные пути к файлам. Я почти уверен, что правильно инициализировал sun_family и sun_path. Я использовал константу макроса и завершил строку символом '\0'. Я также использовал правильную длину для размера адреса при вызове connect().   -  person xQuare    schedule 04.08.2012
comment
@Papergay: Пожалуйста, попробуйте код в этой ссылке pastebin и дайте мне знать, работает ли он у вас, потому что он работает для меня.   -  person jxh    schedule 04.08.2012
comment
@Papergay: попробуйте добавить в свой код цикл while для подключения, как я.   -  person jxh    schedule 04.08.2012
comment
struct sockaddr_un sa; sa.sun_family = AF_UNIX; strncpy(sa.sun_path, mypath, sizeof(sa.sun_path)-1); connect(fd, sa, sizeof(sa)); должен выполнить инициализацию и настройку соединения. Если вы получаете EINVAL, что-то пошло не так до запуска фрагмента кода, показанного выше.   -  person alk    schedule 04.08.2012
comment
В моем комментарии выше должно быть написано: ... connect(fd, &sa, sizeof(sa));   -  person alk    schedule 04.08.2012


Ответы (2)


Из unix_stream_connect исходного кода не видно быть любое требование обнулить структуру. Я написал тестовую программу, которая намеренно заполняет структуру значением, где клиент и сервер передают разные значения заполнения, и они могут нормально подключаться друг к другу:

void init_addr (struct sockaddr_un *addr, const char *path, int I) {
    struct sockaddr_un tmp = { .sun_family = AF_UNIX };
    // fill sun_path
    memset(tmp.sun_path, I, UNIX_PATH_MAX);
    // copy path
    snprintf(tmp.sun_path, UNIX_PATH_MAX, "%s", path);
    *addr = tmp;
}

Причиной сбоя подключения может быть состояние гонки, когда сервер еще не завершил настройку, когда клиент пытается connect.

person jxh    schedule 04.08.2012
comment
После успешного тестирования того самого кода, который раньше у меня не работал, я думаю, вы должны быть правы. Не могли бы вы рассказать об этом немного подробнее? Сколько времени занимает настройка сервера? Если я правильно помню, раньше я создавал клиента даже после вызова listen(). - person xQuare; 04.08.2012
comment
Не следует получать EINVAL в случае, если сервер, к которому подключается, еще не готов. - person alk; 04.08.2012
comment
Какой смысл заполнять sun_path Is? - person alk; 04.08.2012
comment
@Papergay: Не видя вашего кода, мне трудно дать осмысленный ответ. Если добавление цикла while вокруг вашего вызова connect исправит это, я уверен, что это гонка. Вы можете задать другой вопрос, предоставив более подробную информацию о своем клиенте и сервере, если вам нужна помощь в поиске расы. - person jxh; 04.08.2012
comment
@alk: I - это значение заполнения. В моем тестовом коде сервер заполнял структуру 'A', а клиент заполнял структуру 'B'. Но оба устанавливают путь к одному и тому же значению для accept/connect соответственно. - person jxh; 04.08.2012
comment
Хорошо, но мне понадобится некоторое время, чтобы изменить свой код... так как я работал над ним в последние дни... Однако я скоро его обновлю. Спасибо за терпеливость. - person xQuare; 04.08.2012
comment
Эээ, я больше не могу воссоздать ошибку. Я тоже не стал копировать. Но правильно ли то, что alk сказал об ошибке EINVAL, или нет? Если вы уверены, что гонка может вызвать такую ​​ошибку, тогда на мой вопрос дан ответ. - person xQuare; 06.08.2012
comment
@Papergay: я уверен, но первое предложение вашего первоначального вопроса было: как указано в заголовке, мой вызов connect() к сокету типа домена unix с соответствующим адресом приводит к ошибке ENOENT: нет такого файла или каталога. - person jxh; 06.08.2012
comment
Да, это было. И я исправил эту ошибку в то время и задал другой вопрос в своем ответе, но не получил никакого ответа (очевидно). Поэтому я решил спросить об этом в другой теме, потому что мне это показалось очень странным, и решение, которое я нашел, на самом деле не должно было иметь никакого значения. Но это все равно решило ошибку, которая у меня была. Это то, о чем я говорю. - person xQuare; 06.08.2012
comment
@Papergay: Я думаю, что ваша первоначальная проблема была гонкой, и вы представили какую-то другую проблему, когда экспериментировали с размером своей структуры. Из исходного кода есть только три причины, по которым connect возвращает EINVAL. (1) Неверный параметр len, (2) неправильное поле family и (3) сокет не находится в состоянии CLOSED. - person jxh; 06.08.2012
comment
Ладно, думаю, я понимаю, что ты пытаешься сказать. Таким образом, добавление memset() и использование исходного кода просто заставили мой процесс/поток занять больше времени, пока он не достиг вызова connect()? Вот почему я больше не получил ENOENT - к тому времени сервер был настроен. EINVAL нельзя было избежать, так как я все равно проанализировал неправильный размер для вызова connect(). Таким образом, условия гонки просто вызвали ошибку ENOENT, но не ошибку EINVAIL. - person xQuare; 06.08.2012
comment
@Papergay: Да, это то, к чему я пришел из имеющихся доказательств. - person jxh; 06.08.2012

Ответ зависит от нескольких вещей.

Во-первых, если вы не используете анонимные имена (имя файла не начинается с 0), вам не нужно очищать структуру.

Если вы используете анонимные имена, это зависит от того, передаете ли вы параметр «len» в различные вызовы как «sizeof(sockaddr_un)» или что-то вроде «len = strlen(local.sun_path+1) +1 + sizeof(local .sun_family); (Это уродливо, потому что игнорирует возможные проблемы с упаковкой структуры).

Для анонимных имен имя сокета включает все символы после первого 0 до конца структуры (как определено параметром len). Поэтому, если вы передаете len как sizeof(sockaddr_un) (что рекомендуется), вам нужно, чтобы весь sun_path совпадал между двумя вызовами, и поэтому требуется обнуление структуры (или идентичная установка структур).

Для достижения наилучших результатов, удобочитаемости и переносимости вы должны использовать len = sizeof(sockaddr_un) и установить memset sockaddr_un в 0 перед его инициализацией.

Этот пост должен быть полезен: Не удается подключиться к абстрактному сокету Unix Linux

person mark18637    schedule 13.04.2013