Как Вы программируете безопасно за пределами среды управляемого кода?

Это проходит glibc, который выпускает прерывание 0x80 после заполнения регистров с параметрами. Обработчик прерываний ядра затем ищет syscall в syscall таблице и вызывает соответствующий sys_ * () функция.

8
задан 3 revs 7 October 2009 в 15:07
поделиться

9 ответов

Все вышеперечисленное. Я использую:

  1. С особой осторожностью
  2. Интеллектуальные указатели, насколько это возможно
  3. Проверенные структуры данных, множество стандартной библиотеки
  4. Постоянные модульные тесты
  5. Проверка памяти такие инструменты, как MemValidator и AppVerifier
  6. Молитесь каждую ночь, чтобы он не вылетел на сайте клиента.

На самом деле, я просто преувеличиваю. Это не так уж плохо, и на самом деле не так уж сложно сохранить контроль над ресурсами, если вы правильно структурируете свой код.

Интересное примечание. У меня есть большое приложение, которое использует DCOM и имеет управляемые и неуправляемые модули. Неуправляемые модули, как правило, труднее отлаживать во время разработки, но они очень хорошо работают на сайте заказчика из-за большого количества выполняемых на нем тестов.

24
ответ дан 5 December 2019 в 04:30
поделиться

Не менее актуально - как вы убедитесь, что ваши файлы и сокеты закрыты, ваши блокировки сняты, yada yada. Память - не единственный ресурс, и с GC вы по своей сути теряете надежное / своевременное уничтожение.

Ни GC, ни не-GC автоматически не превосходят. У каждого есть свои преимущества, у каждого своя цена, и хороший программист должен уметь справиться с обоими.

Я сказал об этом в ответе на этот вопрос .

13
ответ дан 5 December 2019 в 04:30
поделиться

Я использую C ++ 10 лет. Я использовал C, Perl, Lisp, Delphi, Visual Basic 6, C #, Java и различные другие языки, которые я не могу припомнить.

Ответ на ваш вопрос прост: вы должны знать, что делаете , больше, чем C # / Java. больше, чем - вот что порождает такие разглагольствования, как Джефф Этвуд относительно «Школы Java» .

Большинство ваших вопросов в некотором смысле бессмысленны. «Проблемы», которые вы поднимаете, - это просто факты того, как аппаратное обеспечение действительно работает . Я бы хотел попросить вас написать ЦП и ОЗУ на VHDL / Verilog и посмотреть, как все работает на самом деле, даже когда действительно упрощено. Вы' Я начну понимать, что способ C # / Java - это абстракция, связывающая аппаратное обеспечение.

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

(Я также написал C # и Java)

3
ответ дан 5 December 2019 в 04:30
поделиться

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

Я часто запускаю под Valgrind , и мой код не имеет утечек памяти. Нуль. Гораздо проще сохранить программу без утечек, чем взять программу с ошибками и исправить все утечки.

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

Я пишу чистый C (я не могу использовать C ++ в этом проекте), но я делаю C очень последовательно. У меня есть объектно-ориентированные классы с конструкторами и деструкторами; Приходится вызывать их вручную, но последовательность помогает. И если я забываю вызвать деструктор, Valgrind бьет меня по голове, пока я не исправлю его.

В дополнение к конструктору и деструктору я пишу функцию самопроверки, которая просматривает объект и решает, является ли он нормальным или не; например, если дескриптор файла равен нулю, но связанные с ним данные файла не обнулены, это указывает на какую-то ошибку (либо дескриптор затерт, либо файл не был открыт, но в этих полях объекта есть мусор). Кроме того, у большинства моих объектов есть поле «подпись», в котором должно быть установлено определенное значение (специфичное для каждого отдельного объекта). Функции, использующие объекты, обычно утверждают, что объекты нормальны.

Каждый раз, когда я malloc () немного памяти, моя функция заполняет память значениями 0xDC . Структура, которая не полностью инициализирована, становится очевидной: счетчики слишком велики, указатели недействительны ( 0xDCDCDCDC ), и когда я смотрю на структуру в отладчике, становится очевидно, что она не инициализирована. Это намного лучше, чем заполнение памяти нулями при вызове malloc () . (Конечно, заливка 0xDC есть только в отладочной сборке; нет необходимости для сборки выпуска тратить это время.)

Каждый раз, когда я освобождаю память, я стираю указатель. Таким образом, если у меня есть глупая ошибка, когда код пытается использовать указатель после того, как его память была освобождена, я мгновенно получаю исключение с нулевым указателем, которое указывает мне прямо на ошибку. Мои функции деструктора не принимают указатель на объект, они принимают указатель на указатель и стирают указатель после разрушения объекта. Также, деструкторы стирают свои объекты перед их освобождением, поэтому, если какой-то фрагмент кода имеет копию указателя и пытается использовать объект, утверждение проверки работоспособности срабатывает немедленно.

Valgrind сообщит мне, если какой-либо код записывает конец буфер. Если бы у меня этого не было, я бы поставил «канареечные» значения после концов буферов и проверил их при проверке работоспособности. Эти канареечные значения, как и значения подписи, будут использоваться только для отладки, поэтому в версии выпуска не будет раздувания памяти.

У меня есть набор модульных тестов, и когда я вношу какие-либо серьезные изменения в код, он очень удобно запускать модульные тесты и быть уверенным, что я ничего ужасно не сломал. Конечно, я запускаю модульные тесты как для отладочной, так и для окончательной версии, поэтому у всех моих утверждений есть шанс найти проблемы.

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

Наконец, я должен сказать, что мне действительно нравится венгерская нотация. Я работал в Microsoft несколько лет назад и, как и Джоэл, выучил Apps венгерский, а не сломанный вариант. Это действительно делает неправильный код неправильным .

Должен сказать, что мне действительно нравятся венгерские обозначения. Я работал в Microsoft несколько лет назад и, как и Джоэл, выучил Apps венгерский, а не сломанный вариант. Это действительно делает неправильный код неправильным .

Должен сказать, что мне действительно нравятся венгерские обозначения. Я работал в Microsoft несколько лет назад и, как и Джоэл, выучил Apps венгерский, а не сломанный вариант. Это действительно делает неправильный код неправильным .

16
ответ дан 5 December 2019 в 04:30
поделиться

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

  • Инструмент статического анализа (например, PC-Lint ).
  • Соответствие MISRA-C (обеспечивается инструментом статического анализа).
  • Не выделяется вообще динамическая память.
3
ответ дан 5 December 2019 в 04:30
поделиться

Я написал и C ++, и C #, и я не вижу всей шумихи вокруг управляемого кода.

Да ладно, есть сборщик мусора для памяти, это полезно ... если, конечно, вы не воздержитесь от использования простых старых указателей в C ++, если вы используете только smart_pointers, у вас не будет так много проблем.

Но тогда я бы хотел знать. .. ваш сборщик мусора защищает вас от:

  • сохранения соединений с базой данных открытыми?
  • сохранения блокировок файлов?
  • ...

Управление ресурсами - это гораздо больше, чем управление памятью. Хорошая вещь в C ++ заключается в том, что вы быстро узнаете, что означает управление ресурсами и RAII, так что это становится рефлексом:

  • если мне нужен указатель, мне нужен auto_ptr, shared_ptr или weak_ptr
  • , если я хочу Соединение с БД, мне нужен объект «Соединение»
  • , если я открываю файл, мне нужен объект «Файл»
  • ...

Что касается переполнения буфера, ну, это не похоже на то, что мы используем char * и size_t везде. У нас есть некоторые вещи, называемые 'string', 'iostream' и, конечно же, уже упомянутый метод vector :: at, который освобождает нас от этих ограничений.

Протестированные библиотеки (stl, boost) хороши,

1
ответ дан 5 December 2019 в 04:30
поделиться

Помимо множества полезных советов, приведенных здесь, моим самым важным инструментом является СУХОЙ - Не повторяйся. Я не распространяю код, подверженный ошибкам (например, для обработки выделения памяти с помощью malloc () и free ()) по всей моей кодовой базе. В моем коде есть ровно одно место, где вызываются malloc и free. Он находится в функциях-оболочках MemoryAlloc и MemoryFree.

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

Иногда, когда я читаю здесь вопрос вроде «Я всегда должен следить за тем, чтобы strncpy завершал строку, есть ли альтернатива?»

strncpy(dst, src, n);
dst[n-1] = '\0';

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

char *my_strncpy (dst, src, n)
{
    assert((dst != NULL) && (src != NULL) && (n > 0));
    strncpy(dst, src, n);
    dst[n-1] = '\0';
    return dst;
}

Основная проблема дублирования кода решена - теперь давайте подумаем, действительно ли strncpy является правильным инструментом для работы. Спектакль? Преждевременная оптимизация! И еще одно место для начала после того, как оно окажется узким местом.

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

char *my_strncpy (dst, src, n)
{
    assert((dst != NULL) && (src != NULL) && (n > 0));
    strncpy(dst, src, n);
    dst[n-1] = '\0';
    return dst;
}

Основная проблема дублирования кода решена - теперь давайте подумаем, действительно ли strncpy является правильным инструментом для работа. Спектакль? Преждевременная оптимизация! И еще одно место для начала после того, как оно окажется узким местом.

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

char *my_strncpy (dst, src, n)
{
    assert((dst != NULL) && (src != NULL) && (n > 0));
    strncpy(dst, src, n);
    dst[n-1] = '\0';
    return dst;
}

Основная проблема дублирования кода решена - теперь давайте подумаем, действительно ли strncpy является правильным инструментом для работа. Спектакль? Преждевременная оптимизация! И еще одно место для начала после того, как оно окажется узким местом.

1
ответ дан 5 December 2019 в 04:30
поделиться

В C ++ есть все упомянутые вами функции.

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

C ++ - язык со строгой типизацией. Прямо как C #.

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

Сравнить метод at () (отмечен) с оператором [] (не отмечен).

Да, мы используем модульное тестирование . Точно так же, как вы должны использовать C #.

Да, мы осторожные программисты. Точно так же, как вы должны быть в C #. Единственная разница в том, что подводные камни в двух языках разные.

0
ответ дан 5 December 2019 в 04:30
поделиться

Ответ Эндрю хороший, но я бы также добавил дисциплины в список. Я обнаружил, что после достаточной практики с C ++ вы довольно хорошо понимаете, что безопасно, а что просит велоцирапторов съесть вас. Вы склонны развивать стиль кодирования, который будет удобен при следовании безопасным методам и оставит у вас чувство хиби-джиби, если вы попытаетесь, скажем, вернуть умный указатель обратно на необработанный указатель и передать его чему-то другому.

I Мне нравится думать об этом как об электроинструменте в магазине. Это достаточно безопасно, если вы научитесь правильно им пользоваться и при условии, что вы всегда соблюдаете все правила безопасности. Вы получаете травму, когда думаете, что можете отказаться от защитных очков.

2
ответ дан 5 December 2019 в 04:30
поделиться