Почему нет ошибок при установке размера буфера отправки/получения сокета выше, чем sysctl max?

Почему нет ошибок при установке размера буфера отправки/получения сокета выше, чем sysctl max (как показано ниже)? не является ли ошибка "ожидаемым поведением"?


Мои значения sysctl для сокетов rmem_max и wmem_max установлены на 212992:

net.core.rmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_default = 212992
net.core.wmem_max = 212992
net.ipv4.tcp_rmem = 4096    87380   6291456
net.ipv4.tcp_wmem = 4096    16384   4194304
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096
vm.lowmem_reserve_ratio = 256   256 32

Когда я создаю сокет и пытаюсь установить размер буфера сокета на 64 * 1024 * 1024 (значение больше, чем rmem_max и wmem_max) для отправки/получения:

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/format.hpp>

using boost::asio::ip::udp;
using boost::format;
using namespace std;

int main()
{
    try
    {
        boost::asio::io_service io_service;
        udp::socket socket(io_service, udp::endpoint(udp::v4(), 0));
        udp::resolver resolver(io_service);
        udp::resolver::query query(udp::v4(), "localhost", "7770");
        udp::resolver::iterator iterator = resolver.resolve(query);

        boost::system::error_code error_code;
        socket.set_option(boost::asio::socket_base::send_buffer_size(64*1024*1024), error_code);
        cout << error_code << endl;
        boost::asio::socket_base::send_buffer_size send_buffer_size;
        socket.get_option(send_buffer_size);
        cout << format("send_buffer_size=%s") % send_buffer_size.value() << endl;

        socket.set_option(boost::asio::socket_base::receive_buffer_size(64*1024*1024), error_code);
        cout << error_code << endl;
        boost::asio::socket_base::receive_buffer_size receive_buffer_size;
        socket.get_option(receive_buffer_size);
        cout << format("receive_buffer_size=%s") % receive_buffer_size.value() << endl;
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

Я ожидаю увидеть ошибку, но вместо этого я не получаю никаких ошибок:

system:0
send_buffer_size=212992
system:0
receive_buffer_size=212992

Если установка размера буфера не сообщает об ошибке, это «ожидаемое поведение» для setsockopt(), я думаю, что соответствующий код должен всегда проверять значение после вызова setsockopt() и выдавать мою собственную ошибку или предупреждение.


person Trevor Boyd Smith    schedule 05.10.2017    source источник
comment
Поведение этих опций не стандартизировано. Вам придется реализовать поведение, специфичное для каждой платформы, которую вы поддерживаете. Исторически сложилось так, что были платформы, на которых вы почти никогда не считывали тот же размер, который вы установили, потому что они измеряли размеры не так, как они их задавали.   -  person David Schwartz    schedule 05.10.2017
comment
Я всегда знал, хотя теперь не могу найти, где это написано, что система может скорректировать указанные вами размеры в большую или меньшую сторону, и что вам нужно вызвать getsockopt(), чтобы узнать, каково было фактическое распределение.   -  person user207421    schedule 06.10.2017


Ответы (1)


Почему нет ошибок при установке размера буфера отправки/получения сокета выше, чем sysctl max (как я продемонстрировал ниже)? это не ошибка "ожидаемое поведение"?

POSIX не говорит об этом явно. Он неявно разрешает setsockopt() сбой (возвращая -1 и устанавливая errno) в случае, если указанное значение не может быть установлено для действительного параметра, но этот сценарий не входит в число тех, в которых он требует, чтобы реализации терпели неудачу. В частности, если вы обратитесь к спецификациям, вы не найдете ваш сценарий в списке условий отказа для setsockopt(). Ближайшим кажется «Указанный параметр недействителен на указанном уровне сокета или сокет был закрыт», но недопустимость на указанном уровне сокета может относиться только к самому параметру, а не к указанное для него значение.

Кроме того, его описание параметров буфера приема и буфера отправки характеризует их, в частности, как запросы на установку указанных размеров буфера. Например:

Параметр SO_RCVBUF запрашивает, чтобы буферное пространство, выделенное для операций приема в этом сокете, было установлено равным значению в байтах значения параметра. [...]

Большинство других опций описываются более детерминистически, часто с использованием глагола «устанавливает» вместо «запросы». Возможно, я слишком много читаю, но для меня, если это запрос, то реализация не обязана его соблюдать. Таким образом, успех setsockopt() зависит от того, доставлен ли он запрос, а не от того, был ли запрос удовлетворен.

Вы отмечаете:

Если установка размера буфера не сообщает об ошибке, это «ожидаемое поведение» для setsockopt(), я думаю, что соответствующий код должен всегда проверять значение после вызова setsockopt() и вызывать мою собственную ошибку или предупреждение.

Если вы хотите сообщить о сбое setsockopt() для установки точных размеров буфера, которые вы указали, то вы действительно должны иметь возможность прочитать их обратно через getsockopt() для проверки. POSIX указывает, что значения этих конкретных опций выражают размеры буфера в байтах; поэтому в соответствующей реализации значения, предоставленные setsockopt, сравнимы со значениями, полученными через getsockopt.

Тем не менее, вы можете быть удивлены. Я могу представить себе несколько подходящих вариаций того, как было бы обычным делом, если бы они не совпадали точно.

person John Bollinger    schedule 05.10.2017