правильный способ использовать функцию чтения () GNU / Linux

0

на страницах руководства GNU/Linux функция чтения описывается следующим кратким описанием:

ssize_t read(int fd, void *buf, size_t count);

Я хотел бы использовать эту функцию для чтения данных из сокета или последовательного порта. Если count больше единицы, указатель, предоставленный в аргументе функции, указывает на последний байт, который был прочитан из порта в памяти, поэтому декремент указателя необходим для приведения указателя к первому байту данных. Это опасно, потому что использование его на языке, таком как C++, с его динамическим распределением памяти в контейнерах в зависимости от их размера и пространственных потребностей может привести к повреждению данных в точке возврата из функции read(). Я думал об использовании массива C-стиля вместо указателя. Это правильный подход? Если нет, то каков правильный способ сделать это? Язык программирования, который я использую, - C++.

РЕДАКТИРОВАТЬ:

Код, который вызвал описанную ситуацию, выглядит следующим образом:

Класс QSerialPort использовался для настройки и открытия порта со следующими параметрами:

  • Baudrate 115200
  • 8 бит данных
  • Отсутствие паритета
  • Один стоповый бит
  • Без контроля потока

и для части чтения, если речь идет о потоке stackoverflow, чтение выполняется точно так же, как это:

std::vector содержащий ряд struct определенных следующим образом:

struct DataMember 
{
    QString name;
    size_t  count;
    char *buff;
}

то в течение цикла while до достижения конца указанного std::vector, read() выполняется на основе переменной члена count указанной struct и данные сохраняются в том же struct buff:

ssize_t nbytes = read(port->handle(), v.at(i).buff, v.at(i).count);

а затем данные печатаются на консоли. В моем тестовом примере, пока данные являются одним байтом, значение напечатано правильно, но для более чем одного байта отображаемое значение является последним значением, которое было прочитано из порта, а также некоторыми значениями мусора. Я не знаю, почему это происходит. Обратите внимание, что правильный результат получается, когда char *buff изменяется на char buff[count].

  • 2
    Я не совсем уверен, почему вы думаете, что указатель должен двигаться? Параметр count сообщает функции, насколько велик массив, на который указывает buf , чтобы он не перезаписывал конец массива.
  • 3
    Указатель указывает на первый байт данных (если какие-либо данные читаются).
Показать ещё 7 комментариев
Теги:
pointers

3 ответа

0
Лучший ответ

Я не знаю, почему это происходит.

Но я знаю. char * - это просто указатель, но этот указатель должен быть инициализирован чем-то, прежде чем вы сможете его использовать. Без этого вы вызываете неопределенное поведение, и все может случиться.

Вместо значения size_t count; и char *buff вам нужно просто использовать std::vector<char>, прежде чем делать вызов чтения, изменить его размер до количества байтов, которые вы хотите прочитать, затем взять адрес первого элемента этого вектора и передать это читать:

struct fnord {
    std::string name;
    std::vector data;
};

и использовать его так: что использование read требует некоторой дополнительной работы для правильной обработки сигналов и условий ошибки.

size_t readsomething(int fd, size_t count, fnord &f)
{
    // reserve memory
    f.data.reserve(count);

    int rbytes = 0;
    int rv;
    do {
        rv = read(fd, &f.data[rbytes], count - rbytes);
        if( !rv ) {
            // End of File / Stream
            break;
        }
        if( 0 > rv ) {
            if( EINTR == errno ) {
                // signal interrupted read... restart
                continue;
            }
            if( EAGAIN == errno
             || EWOULDBLOCK == errno ) {
                // file / socket is in nonblocking mode and
                // no more data is available.
                break;
            }
            // some critical error happened. Deal with it!
            break;
        }
        rbytes += rv;
    } while(rbytes < count);
    return rbyteS;
}

Глядя на ваш первый абзац тарабарщины:

Если счетчик больше единицы, указатель, указанный в аргументе функции, указывает на последний байт, который был прочитан из порта в памяти

Что заставляет вас думать так? Это не так, как это работает. Скорее всего, вы передали какой-то неверный указатель, который не был правильно инициализирован. Все может случиться.

поэтому декремент указателя необходим для приведения указателя к первому байту данных.

Неа. Это не так, как это работает.

Это опасно, потому что использование его на языке, таком как C++, с его динамическим распределением памяти в контейнерах в зависимости от их размера и пространственных потребностей может привести к повреждению данных в точке возврата из функции read().

Неа. Это не так, как это работает!

C и C++ являются явными языками. Все происходит просто, и ничего не происходит без вас (программист), явно запрашивающего его. Не выделяется память без запроса об этом. Это может быть либо явный new, некоторый RAII, автоматическое хранение или использование контейнера. Но ничего не происходит "из ничего" в C и C++. Там нет встроенной сборки мусора ^ 1 в C или C++. Объекты не перемещаются в памяти или не изменяют размер, если вы явно не кодируете что-то в свою программу, чтобы это произошло.


[1]: Существуют библиотеки GC, которые вы можете использовать, но они никогда не будут топать на все, что может быть достигнуто с помощью выполняемого кода. По сути библиотеки сборщиков мусора для C и C++ являются детекторами утечки памяти, которые освобождают память, которая больше не может быть достигнута при нормальном потоке программы.

3

Если счетчик больше единицы, указатель, указанный в аргументе функции, указывает на последний байт, который был прочитан из порта в памяти

Нет. Указатель передается методу read() по значению, поэтому совершенно невозможно, чтобы значение было иным после вызова, чем было раньше, независимо от количества.

поэтому декремент указателя необходим для приведения указателя к первому байту данных.

Указатель уже указывает на первый байт данных. Никакой декремент не требуется.

Это опасно, потому что использование его на языке, таком как C++, с его динамическим распределением памяти в контейнерах в зависимости от их размера и пространственных потребностей может привести к повреждению данных в точке возврата из функции read().

Это все ерунда, основанная на невозможности.

Вы ошибаетесь во всем этом.

  • 0
    Как вы думаете, я бы задал этот вопрос, если бы такой ситуации не было? использование массивов в стиле C, как указано в комментариях Джонатана, решило проблему, и данные были именно такими, как я ожидал, но с помощью указателя я мог видеть, что мои ожидаемые данные существуют в памяти, но указатель не указывает на начало.
  • 5
    Ситуация, которую вы описали, физически, логически и семантически невозможна. Ergo этого не произошло , как вы описали. Если вы разместите какой-то код, вам могут рассказать, что на самом деле произошло. Простое утверждение не обрежет его.
Показать ещё 1 комментарий
2

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

Из прочитанной (2) man-страницы:

При успехе возвращается количество прочитанных байтов (нуль указывает конец файла), а позиция файла продвигается по этому номеру. Это не ошибка, если это число меньше количества запрошенных байтов; это может произойти, например, из-за того, что на данный момент доступно меньше байтов (возможно, потому, что мы были близки к концу файла или потому, что мы читаем из канала или с терминала), или потому, что read() был прерван сигнал. При ошибке возвращается -1, а errno устанавливается соответствующим образом. В этом случае не указывается, изменяется ли позиция файла (если таковая имеется).

В случае труб, сокетов и символьных устройств (включая последовательные порты) и чтения файлового дескриптора блокировки (по умолчанию) на практике не дожидается полного счета. В вашем случае read() блокирует, пока байт не войдет в последовательный порт и не вернется. Вот почему на выходе первый байт правильный, а остальное - мусор (неинициализированная память). Вы должны добавить цикл вокруг read(), который повторяется до тех пор, пока количество байтов не будет считано, если вам нужен полный счетчик.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню