LINUX.ORG.RU

Почему надо проверять malloc на NULL

 , ,


1

3

Вообще выделение памяти проверять.

А вот потому что:

#include <stdio.h>
int main()
{
  unsigned char *a = NULL;
  int b = 0;
  unsigned long n = (unsigned long)&b;
 
  printf("b = %d, n = %lu\n",b,n);
  a[n]=1;
  printf("b = %d\n",b);
  return 0;
}

Запуск

$ gcc nullptr.c -o nullptr
$ ./nullptr 
b = 0, n = 140731726099148
b = 1

Запускалось в 64-битном (x86_64) Linux. Как видно, никаких сегфолтов и прочих ошибок. Конечно, адрес в районе 127 Тб в примере далеко за пределами доступного почти на всех компьютерах, но нет никаких гарантий, что на какой-то системе с каким-то компилятором и настройками среды значение не окажется более доступным. Могут быть и другие архитектуры (32-битные например), если запускать от root'а, то в начало может быть разрешена запись и там иметься память процесса. Или ещё какие-то варианты.

shdown, monk, liksys, Xenius - я думаю вам понравится. Пример сочинился по ходу чтения обсуждения Вышло издание 2,92 книги «Программирование: введение в профессию» А. В. Столярова (комментарий)

★★★★★

Последнее исправление: anonymous_incognito (всего исправлений: 1)
Ответ на: комментарий от bugfixer

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

Ну и какой же вывод из этих двух взаимоисключащих наблюдений должно сделать?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ну и какой же вывод из этих двух взаимоисключащих наблюдений должно сделать?

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

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Повторю еще раз: есть класс задач, в которых NULL из malloc-а должен вести к немедленному abort-у и это лучшая реакция. Есть класс задач, в которых так быть не должно в принципе.

нет универсальных правил и рецептов которые работают всегда и для всех.

Какой сценарий не покрыт и почему предложенные рецепты не сработают?

r--r--r--
()
Ответ на: комментарий от r--r--r--

Падать - это то что делаем мы. Не падать - а вот здесь начинаются сказки венского леса потому как при сколь-нибудь значимом размере софта оттестировать вы это нормально не сможете, и будете продавать «надежду», ага - до первой нештатной ситуации.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Я не увидел ответа на вопрос:

Какой сценарий не покрыт и почему предложенные рецепты не сработают?

при сколь-нибудь значимом размере софта оттестировать вы это нормально не сможете,

Движок базы данных оракла мы записываем в какую категорию:

  1. Не нормально оттестирован
  2. Нисколько не значимый размер

?

r--r--r--
()
Ответ на: комментарий от bugfixer

вами

Он перестал существовать или что с ним случилось?

Next

Я не увидел ответа на вопрос:

Какой сценарий не покрыт и почему предложенные рецепты не сработают?

r--r--r--
()
Ответ на: комментарий от r--r--r--

Он перестал существовать или что с ним случилось?

У вас никогда не будет ресурсов сопоставимых с тем что на этот движок было потрачено. Я не вижу смысла это обсуждать.

Какой сценарий не покрыт и почему предложенные рецепты не сработают?

Что за детский сад? Я не вижу ни «рецептов», ни «сценариев».

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer
  1. Падать.
  2. Не падать.

Я не вижу ни «рецептов», ни «сценариев».

Чем эти два не подходят?

У вас никогда не будет ресурсов

У меня лично - да. Но я буду честным, если бы они у меня были, не факт, что я бы потратил бы их на решение именно этой проблемы. Тем не менее.

Я не вижу смысла это обсуждать.

  1. Вы уже написали больше 10 постов в посвящённой именно этому вопросу теме.

  2. Поскольку нам не удаётся вернутся к интересующему пункту путём диалога, я переформулирую вопрос:

Почему вариант "всегда и везде падать" как и вариант "всегда и везде обрабатывать" не были названы "юношеским максимализмом" и " универсальным правилом и рецептом которые работают всегда и для всех", а вариант "поступать в зависимости от ситуации" – был?

r--r--r--
()
Последнее исправление: r--r--r-- (всего исправлений: 2)
Ответ на: комментарий от MKuznetsov

Общий случай, где-то внутри процесса malloc вернул NULL (hip`а нет, память фрагментирована и мелкие free() не спасут, если embed то вот вот и стек исчерпается) , что и как ему делать ? где будешь ловить и как обрабатывать ошибку.. как будешь проверять ??

Вопрос не в том как обрабатывать NULL, а в том, что хоть как-то, но надо обрабатывать. Иначе поведение программы становится потенциально непредсказуемым, потому что не обязательно программа будет прибита OOM или сегфолтнется, она может продолжить как ни в чём не бывало работать и портить данные.

anonymous_incognito ★★★★★
() автор топика
Ответ на: комментарий от bugfixer

ни противоречия ни взаимоисключения я здесь не вижу

Вы признаете, что где-то abort после NULL из malloc неприемлем, но придерживаетесь собственного мнения, что abort в таком случае – это самое лучшее.

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

Т.е. мнение о том, что решать abort или нет нужно не сразу при выходе из malloc, а там, где это уместнее всего – это юношеский максимализм? Тогда как «abort сразу после malloc» нет?

eao197 ★★★★★
()
Ответ на: комментарий от anonymous_incognito

Вопрос не в том как обрабатывать NULL, а в том, что хоть как-то, но надо обрабатывать.

выше, несколько раз указали на g_malloc. Там разумное поведение. «в самом низу, как только обнаружилась ошибка по памяти (нет её) - сразу падаем через g_die, там запись в журнал и возможно фиксация баз/файлов». Если невозможно использовать glib, то намонстрячить свой аналог. Ни в коем случае не откаты по месту или передача NULL дальше по цепочке.

MKuznetsov ★★★★★
()
Ответ на: комментарий от eao197

Вы признаете, что где-то abort после NULL из malloc неприемлем

готов согласиться

но придерживаетесь собственного мнения, что abort в таком случае – это самое лучшее.

В НАШИХ(!!) use-case’ах. Я что, заставляю кого-то делать то же самое? Я просто выражаю здоровый скепсис по поводу качества заявленной обработки нештатных ситуаций по памяти, если кто-то пытается это «продать».

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от bugfixer

В НАШИХ(!!) use-case’ах.

Я надеялся, что эта оговорка появится, т.к. ранее ее не было. Например, здесь:

если обработка нетривиальна - а как оно вообще тестировалось? При попытке «продать» мне вот это я бы требовал доказательств демонстрирующих что абсолютно каждый из этих «if»ов был hit во время тестирования. Было бы даже интересно посмотреть как народ выкрутиться пытался бы.

и вот здесь:

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


Я что, заставляю кого-то делать то же самое?

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

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

Здесь есть некая подмена тезиса: из ваших слов может следовать, что либо делается «правильный» abort сразу после получения NULL из malloc-а, либо же делается какая-то «заявленная обработка». Между тем ваши оппоненты говорят не про обработку NULL прямо здесь и сейчас, а об отказе звать abort здесь и сейчас. Моя позиция – встретился NULL – верни его наверх, пусть там разбираются. Если откат и восстановление выше сделать не могут, то все в итоге к abort-у и придет.

Что до тестирования, то для более-менее сложного софта, имхо, 100% тестирование – это утопия. По крайней мере в тех некритичных областях, с которыми мне доводилось соприкасаться (софт для ядреных станций или самолетов не писал).

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Моя позиция – встретился NULL – верни его наверх, пусть там разбираются. Если откат и восстановление выше сделать не могут, то все в итоге к abort-у и придет.

Позиция понятна, и имеет право на жизнь. Кто-то на это смотрит как на «свободу выбора», а кто-то как на отказ взять на себя ответственность за принятые решения.

пусть там разбираются

И всё таки интересно: если «там» тоже вы - это как то повлияет на выбор?

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

а кто-то как на отказ взять на себя ответственность за принятые решения.

Это непонятный для меня тезис.

Допустим, есть автор условной libcurl. Он взял на себя ответственность за то, что NULL при аллокации памяти – это abort.

Допустим, есть архитектор проекта (например, прокси-сервервера с 100K одновременных долго живущих соединений), который решает, что NULL при аллокации памяти в рамках обслуживания одного соединения – это ни в коем случае не abort, а команда для закрытия этого конкретного соединения.

Допустим, есть разработчик, который в рамках доработки этого самого прокси-сервера вставил функциональность для периодической загрузки статистики на указанный в конфигурации узел и сделал это посредством той самой условной libcurl, которая зовет abort.

Про чью ответственность идет речь?

если «там» тоже вы - это как то повлияет на выбор?

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Про чью ответственность идет речь?

если «там» тоже вы - это как то повлияет на выбор?

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

Усложним - NULL прилетел в потоке порождённом вашей библиотечкой о котором клиенты библиотечки вообще ничего не знают и знать не должны. Что должно происходить?

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 2)
Ответ на: комментарий от bugfixer

Усложним - NULL прилетел в потоке порождённом вашей библиотечкой о котором клиенты библиотечки вообще ничего не знают и знать не должны. Что должно происходить?

Сложно разговаривать о сферических конях в вакууме :(

Предположим, что этот поток выполнял какую-то операцию, в процессе выполнения возникло препятствие в виде NULL из malloc-а. Значит операция продолжена быть не может. Значит нужно ее завершить без утечки каких-либо ресуров (а лучше бы с откатом в исходное состояние, но это уже сложнее и не всегда возможно). А пользователю библиотеки ошибка доставляется каким-то способом, приятным внутри этой библиотеки.

Двайте поговорим о чем-то более предметном. Представим, что у нас есть библиотека, реализующая контейнер по типу «разреженный индекс». Т.е. у нас двухуровневая структура данных – на первом уровне оглавление, на втором уровне «страницы» со значениями. Операция вставки в такой контейнер может потребовать две аллокации: на создание новой «страницы» и на создание нового оглавления, если старое уже заполнено.

Есть приложение, которое использует такой контейнер из этой библиотеки. Делается очередная вставка и какая-то из этих двух операций malloc-а возвращает NULL. Нормально ли здесь вызывать abort?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Есть приложение, которое использует такой контейнер из этой библиотеки. Делается очередная вставка и какая-то из этих двух операций malloc-а возвращает NULL. Нормально ли здесь вызывать abort?

да. падать надо при первом-же обнаружении ошибки, сразу. И не откладывать в надежде «пусть на@#тся из другого места». Потому-что ошибку памяти ты не поборешь, она остаётся

MKuznetsov ★★★★★
()

Могут быть и другие архитектуры (32-битные например)

У меня 32-битный Дебиан-11, запустил на нем, от юзера:

b = 0, n = 3213627860
b = 1

Тоже сильно большое число получилось.

watchcat382 ★★
()
Ответ на: комментарий от MKuznetsov

падать надо при первом-же обнаружении ошибки, сразу.

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

watchcat382 ★★
()
Ответ на: комментарий от bugfixer

Усложним - NULL прилетел в потоке порождённом вашей библиотечкой о котором клиенты библиотечки вообще ничего не знают и знать не должны. Что должно происходить?

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

anonymous_incognito ★★★★★
() автор топика
Последнее исправление: anonymous_incognito (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

Потому-что ошибку памяти ты не поборешь, она остаётся

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

anonymous_incognito ★★★★★
() автор топика
Ответ на: комментарий от watchcat382

Тоже сильно большое число получилось.

Гкхм... Вот тогда код, в котором у меня относительно небольшое число на 64-битном получилось. На этот раз портим код функции main() в памяти.

Пример уже слегка искусственный, потому что пришлось специально озаботиться разрешить запись в секцию кода, без чего программа падает с segfault (но кстати на 32 битах может и не всегда понадобится специально разрешать) и компилировать с опцией -no-pie, без которой число будет очень большим. Но теоретически можно представить и такую программу, в которой есть JIT-компиляция и нужно отключить позиционную независимость кода (-no-pie опцией).

Притом сделать writable нулевую область памяти таким способом не удастся, так что такая вещь - это не отключение вообще сегфолта при нулевом указателе.

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdint.h>

void make_memory_writable(void *addr)
{
    long pagesize = sysconf(_SC_PAGESIZE);
    // Align for page size (typicaly 4096)    
    uintptr_t page_start = (uintptr_t)addr & ~(pagesize - 1);
    
    // Set permissions for READ, WRITE and EXEC for page
    if (mprotect((void *)page_start, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
    {
        perror("mprotect failed");
    }
}

int main()
{
   unsigned char old, *a = NULL;
   unsigned long n;
   n = (unsigned long)&main; /* address of main fumction */
   printf("main[0] before write %u, n =  %lu\n",a[n],n);
   old = a[n]; /* backup value */
   make_memory_writable(a+n);
   a[n]=1;
   printf("main[0] after write %u\n",a[n]);
   a[n]=old;
   return 0;
}

Запуск

$ gcc -no-pie nullptr2.c -o nullptr2
$ ./nullptr2 
main[0] before write 85, n =  4198835
main[0] after write 1
anonymous_incognito ★★★★★
() автор топика
Последнее исправление: anonymous_incognito (всего исправлений: 1)
Ответ на: комментарий от anonymous_incognito

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

вроде бы взрослый человек, а веришь в милости от природы :-) Критическая ошибка может только усугубляться. Ожидать что «вот я сейчас тут поосвобождаю и всё прокатит» довольно наивно, потому-что не прокатит. Всё вскорости повторится, выпрёт в другом месте и с худшими последствиями.

на таких ошибках действия такие-же как по сигналу (по хорошему вообще один код) - переключил в аварийный контекст, там с ограниченным набором функций (без malloc|free кстати) кинул диагностику, по возможности что-то сохранил и всё. Упал. Разве что кости не сломаны

MKuznetsov ★★★★★
()
Ответ на: комментарий от anonymous_incognito

Конечно, специфика malloc в том, что если вдруг памяти резко стало не хватать, система вероятно близка к краху.

Система - вовсе не обязательно, а вот процессу однозначно плохо. Наверное придётся таки написать длинный пост, что для меня крайне нехарактерно. Устраивайтесь поудобнее. Итак…

У подхода «падать сразу» есть масса неоспоримых преимуществ:

  1. Простота (тут всё понятно)
  2. Детерминированность (тут тоже)
  3. Impact. Народ любит всякие медицинские вещи в пример приводить, а мне вот не очевидно что условная «остановка сердца на 15 секунд с дальнейшей нормальной его работой» хуже чем провал в «фиксированные 40 ударов в минуту на последующие 24 часа».
  4. Visibility. Падающий процесс трудно замести под ковёр. Ваши operations об этом будут знать, а соответственно и вы об этом будете знать и сможете отреагировать.
  5. Уход от ментальности «это они виноваты, а у меня лапки». Если вы допустили что у процесса закончилась память - вы, именно ВЫ а не кто-то ещё, что-то делаете не так. И с этим нужно разбираться. И хорошо если это какой-то несуразно длинный rogue allocation из-за недостаточной валидации входных данных и/или запросов (это вы легко пофиксите), может вы в принципе кешируете слишком много, может у вас в памяти куча ненужного мусора болтается, может на машинке памяти неадекватно мало для её задач. Этот список бесконечен на самом деле. Но главный поинт в том что разбираться придётся, и именно вам.

Остановлюсь-ка, пожалуй, я пока на этом…

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от bugfixer

1.2.3.4.

Все эти аргументы ничего не стоят если рестарт занимает одинадцать часов. И нет, это не медицина. Это инструмент для финансовой аналитики с многодесятилетней историей от очень и очень большой корпорации. Там особенность предметной области такая – все данные в память нужно загружать.

Если вы допустили что у процесса закончилась память - вы, именно ВЫ а не кто-то ещё, что-то делаете не так.

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

Но если речь идет о софте, который разные пользователи будут гонять в совершенно разных условиях, то… Вот попробует открыть кто-нибудь на нетбуке с 4Gb RAM Word-овский документ на 3000 страниц с сотней картинок по 100Mb каждая. И откуда брать память разработчикам Word-а?

Или вот тот же вышеупомянутый инструмент для финансовой аналитики. С ним одновременно работает от нескольких десятков до нескольких сотен пользователей. Каждый пользователь может запускать разнообразные запросы, каждый запрос может потреблять под себя разное количество памяти. Расчитать все так, чтобы для всех пользователей всегда хватало… Это что-то из области фантастики. И если в час пик, когда одновременно 200 человек запустили свои запросы и сервер выдал все, что мог, десятку из пользователей сообщат «Извините, сейчас ваш запрос обработан быть не может, сервер перегружен», то это более чем норм. А вот мнгновенное падение с последующим многочасовым рестартом… Ну такое себе.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

«Извините, сейчас ваш запрос обработан быть не может, сервер перегружен», то это более чем норм. А вот мнгновенное падение с последующим многочасовым рестартом… Ну такое себе.

а до наступания на грабли malloc()==NULL вы такое сказать не в силах ? вот это вот действительно «такое себе»..допустил до исчерпания памяти процесса и всё сломал. Произвольный пользователь получит кривой результат или не получит вовсе и далее по кругу. Сервис больше не работает, зато я_не_виноват

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

а до наступания на грабли malloc()==NULL вы такое сказать не в силах ?

Если бы вы не были невменько, то у вас можно было бы спросить, а почему malloc()==NULL – это для вас грабли.

Но у вас это спрашивать бесполезно.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Если бы вы не были невменько, то у вас можно было бы спросить, а почему malloc()==NULL – это для вас грабли. Но у вас это спрашивать бесполезно.

у тебя хоть один аргумент есть за то что malloc()==NULL это прекрасно и легко оборимо ?

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

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

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

Почти наверняка у вас в коде-гайде ещё забито «обнулять память после malloc», потому-что это обязательное следствие «всегда проверять malloc и откатывать при NULL». Всё ваша фин.аналитика в результате занимается сравнением с NULL и обнулением памяти в основном.

MKuznetsov ★★★★★
()
Ответ на: комментарий от eao197

Если бы вы не были невменько, то у вас можно было бы спросить, а почему malloc()==NULL – это для вас грабли.

Господин прав на самом деле, и я с ним солидарен (сам хотел ответить другими словами но в том же ключе, но он меня опередил). Раз за разом вы упорно отказываетесь признавать проблему и вместо того чтобы разруливать root-cause (не надо доводить до того что память кончается) - отлыниваете от ответственности и предлагаете латать последствия довольно кустарными методами.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Т.е. разговора по существу не будет, все приводившиеся выше примеры (прокси-сервер, сервер СУБД, инструмент для финансовой аналитики) вы отправляете лесом, я правильно понимаю?

Про хоть какое-то раскрытие тезиса «отлыниваете от ответственности» я вообще даже и не заикаюсь.

eao197 ★★★★★
()
Ответ на: комментарий от MKuznetsov

Всё ваша фин.аналитика в результате занимается сравнением с NULL и обнулением памяти в основном.

Такое комментировать только портить.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

все приводившиеся выше примеры (прокси-сервер, сервер СУБД, инструмент для финансовой аналитики)

… притянуты за уши.

Про хоть какое-то раскрытие тезиса «отлыниваете от ответственности» я вообще даже и не заикаюсь.

Вы серьёзно??

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

… притянуты за уши.

Ничего подобного. Прокси-сервер и инструмент для финансовой аналитики – это из моей собственной практики. Про сервер я вроде бы даже на Хабре статью публиковал. А с финансовой аналитикой такое дело: небольшая амбициозная компания пытается импортозаместить этот инструмент собственной разработкой и я там по мере сил помогаю. Работающий прототип уже есть, по ряду параметров идет вровень с конкурентом.

Вы серьёзно??

Абсолютно. Мне бы хотелось узнать как эксперты решают проблемы исчерпания памяти во всех упомянутых выше типах продуктов: в прокси-сервере, где неизвестно сколько соединений придется принять; в условном Word-е, в котором могут открывать любые по объему/сложности документы; в сервере СУБД где неизвестно сколько и каких запросов будут присылать клиенты; в платформе для финансовой аналитике, где незвестно столько данных потребуется перелопатить для одновременно выдаваемых запросов… И все это в условиях, когда разработчик софта вообще не знает какими мощностями располагает пользователь и какую нагрузку пользователь обеспечит.

Пока что вы от конкретных вопросов очень умело уворачиваетесь.

eao197 ★★★★★
()
Ответ на: комментарий от MKuznetsov

А я вот всё-таки прокомментирую.

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

Во-первых, оверхед от самого malloc не может сравниться с проверкой на NULL ни в коем случае. Как минимум malloс должен залочить арену, а для этого нужно взять futex, потом его отпустить (это уже две атомарных операции). А ещё обойти свои структуры данных, что часто, как раз-таки, плохо по кэшу. А в худшем случае он сделает системный вызов.

Во-вторых, эти проверки будут хорошо предсказываться branch predictor’ом. Если используется C++, то, скорее всего, там вообще бросается исключение и malloc на NULL проверяется только один раз. А код для случая «вылетело исключение» компилятор как раз помещает в самую жопу.

Почти наверняка у вас в коде-гайде ещё забито «обнулять память после malloc», потому-что это обязательное следствие «всегда проверять malloc и откатывать при NULL».

И почему же это «обязательное следствие»? Типа, убедиться, что ядро эти страницы виртуальной памяти связало с физическими? Наркоман штоле?

shdown ★★
()
Последнее исправление: shdown (всего исправлений: 1)
Ответ на: комментарий от shdown

эти проверки будут хорошо предсказываться branch predictor’ом

не то чтобы сильно хорошо и код «отката» всё равно займёт место в кеше. А вот аттрибуты returns_nonnull аллокатору и noreturn для my_abort() помогут оптимизатору.

И почему же это «обязательное следствие»?

чтобы откатывать, все динамические структуры должны быть гарантированно корректно связаны. Ни один указатель (ни тот по которому собираешься сохранить результат malloc ни те которые потенциально в нём, дочери-родители-братья-сёстры) не должен указывать в мусор. Никогда, даже «я вот тут ещё чуток сделаю, потом всё равно положу правильное значение». А «чуток» вырастает или сразу содержит ошибку

Поэтому если предполагаются откаты то в коде-гайде проекта пишут «получил память - обнули»

MKuznetsov ★★★★★
()
Ответ на: комментарий от dynamic_cast

Какой нахрен оверхед от проверки на NULL. Сам malloc занимает кучу времени. Бранчи в наше время это зироу-кост..

вот так нынешний код и тормоззззиитт ;-) от зироу-кост

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

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

MKuznetsov:

эти проверки будут хорошо предсказываться branch predictor’ом

не то чтобы сильно хорошо

MKuznetsov:

ветка по NULL это-ж пипец как исключительная редкость

Как же это так?

shdown ★★
()
Ответ на: комментарий от MKuznetsov

Вы не понимаете, о чём рассуждаете. Просто порядки величин не представляете.

Попробуйте найти значения N, M и RANDOMIZE, чтобы была хоть какая-то разница между CHECK=0 и CHECK=1. У меня не получилось этого сделать.

#include <stdlib.h>

// N: number of iterations
// M: allocation size
// CHECK: whether to check the result of malloc(), either 0 or 1
// RANDOMIZE: whether to inject random failures into malloc(), either 0 or 1

#if !defined(N) || !defined(M) || !defined(CHECK) || !defined(RANDOMIZE)
# error "You are doing it wrong."
#endif

#define USED(ptr) \
    __asm__ __volatile__ ( \
        "" \
        : \
        : "r"(ptr) \
        : "memory" \
    )

#define DUP2(x) x x
#define DUP4(x) DUP2(x x)
#define DUP8(x) DUP4(x x)

#define DO_SOMETHING_1() \
    __asm__ __volatile__ ( \
        DUP8("addq $1, %%rax\n") \
        : \
        : \
        : "rax", "cc" \
    )

#define DO_SOMETHING_2() \
    __asm__ __volatile__ ( \
        DUP8("subq $1, %%rax\n") \
        : \
        : \
        : "rax", "cc" \
    )

#if RANDOMIZE
static inline size_t maybe_inject_failure(size_t n)
{
    // MINSTD pseudo-random number generator, year 1990 version.
    static unsigned x = 43214321;
    x = (48271ull * x) % 2147483647;

    if (x & 1) {
        return -1;
    }
    return n;
}
#endif

int main(void)
{
    for (int i = 0; i < N; ++i) {
#if RANDOMIZE
        void *ptr = malloc(maybe_inject_failure(M));
#else
        void *ptr = malloc(M);
#endif

        USED(ptr);

#if CHECK
        if (ptr == NULL) {
            DO_SOMETHING_1();
            continue;
        }
#endif

        DO_SOMETHING_2();
        free(ptr);
    }
    return 0;
}
shdown ★★
()
Ответ на: комментарий от bugfixer

Пора заходить с козырей.

Про хоть какое-то раскрытие тезиса «отлыниваете от ответственности» я вообще даже и не заикаюсь.

Вы серьёзно??

Будут ли взрослые и ответственные люди, взявшие на себя всю ответственность, делать abort если NULL нам вернул fopen?

r--r--r--
()
Ответ на: комментарий от anonymous_incognito

Пример уже слегка искусственный

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

watchcat382 ★★
()
Ответ на: комментарий от eao197

Пока что вы от конкретных вопросов очень умело уворачиваетесь.

Постараюсь ответить на то на что можно ответить лаконично.

неизвестно сколько соединений придется принять … неизвестно сколько и каких запросов … незвестно столько данных потребуется … разработчик софта вообще не знает …

Неужели вам не очевидно что основная проблема именно в этом?? Сначала вы выясняете с чем придётся иметь дело, и только потом у вас появляются вменяемые оценки какие мощности потребуются чтобы эти хотелки покрыть, и никак иначе.

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

прототип

если рестарт занимает одинадцать часов

Мне довольно трудно описать словами насколько дико для меня это звучит. И ещё большей дикостью для меня является то что ВЫ считаете это нормой. В текущем виде это просто нежизнеспособный продукт, вот и всё.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от bugfixer

Неужели вам не очевидно что основная проблема именно в этом?? Сначала вы выясняете с чем придётся иметь дело, и только потом у вас появляются вменяемые оценки какие мощности потребуются чтобы эти хотелки покрыть, и никак иначе.

Как же упорно вы отрицаете объективную реальность! Повторю еще раз: вы, как разработчик, не имеете никакого контроля за тем, как написанные вами продукт эксплуатируется. Ну вот вообще.

Разработчик условного nginx или envoy может сказать: в среднем на одно соединение уходит 10KiB. Исходя из этих знаний эксплуатант может расчитать мощность серверов для своих нагрузок. Но разработчик условного nginx не может проконтролировать адекватность этих расчетов. Если условный nginx запустили на железе, которое тянет только 10K соединений, то разработчик условного nginx не несет ответственности за то, что это железо не может потянуть 100K.

Однако, разработчик условного nginx-а несет ответственность за то, что происходит при чрезмерных нагрузках. Когда 10K содинений стабильно обслуживаются, а все лишние стабильно отфутболиваются – это одно. Когда условный nginx стабильно рестартует при появлении (10K+1) соединения – это другое.

И, как по мне, «у меня лапки» относится именно к ситуации с гарантированными падениями при появлении (10K+1) соединения.

В точно такой же ситуации находятся разработчики офисных пакетов (для Word-а можно определить некие минимальные требования к железу, но это не означает, что Word на минимально достаточном железе сможет открыть документы, для которых нужен мощный комп с 64GiB RAM), разработчики серверов СУБД, разработчики MQ-брокеров, разработчики IDE и пр. типов программных продуктов, которые можно охарактеризовать как «коробка». Но зато все эти продукты легко могут оказаться в ситуации острой нехватки памяти на машине пользователя.

Мне довольно трудно описать словами насколько дико для меня это звучит.

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

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

Одинадцать часов рестартует не разрабатываемый сейчас в рамках импортозамещения прототип. А именно что оригинал, история развития которого идет, емнип, с 1980-х (ну с 1990-х точно). Т.е. в одной из крупных российских корпораций сервер этого продукта стартует около одинадцати часов. Делают такие рестарты несколько раз в год, в рамках специальных регламентов, в специально отведенное время (если только он сам по себе не упал, что таки случается).

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

Дико для вас это звучит или нет – фиолетово. Вот есть такое и ничего вы с этим не сделаете.

И ещё большей дикостью для меня является то что ВЫ считаете это нормой.

Я считаю это объективной реальностью.

В текущем виде это просто нежизнеспособный продукт, вот и всё.

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

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Повторю еще раз: вы, как разработчик, не имеете никакого контроля за тем, как написанные вами продукт эксплуатируется. Ну вот вообще.

Плохо вам. Бегите оттуда.

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

Вот. Кто мешает посмотреть в рантайме на чём процесс запустили и заранее отфутболивать соединения сверх гарантируемо обслуживаемых не доводя до OOM?? Вы чего-то недоговариваете.

Я считаю это объективной реальностью.

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

ПыСы. Это не случай когда пожмотничали и не проплатили полную лицензию? Это многое бы объясняло…

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 2)
Ответ на: комментарий от bugfixer

Если надо упасть сразу после NULL-а то эта логика конечно встраивается в аллокатор. И аллокатор с такой логикой означает что NULL мы проверили. Но наличие malloc-а со встроенной проверкой не означает что нельзя при этом пользоваться malloc-ом без проверки и обрабатывать NULL самостоятельно.

Я использую оба варианта, и соображения обычно касаются не итоговой логики приложения, а зон ответственности кода. Например: если речь про консольную программу разового запуска, то непосредственно из её кода вполне норм вызывать аллокацию со встроенной проверкой. Если же речь про какую-нить библиотеку, то засовывать в неё abort в неочевидных местах нехорошо, эту обработку надо делегировать вызывающему коду. Ну или делать два вида апи - один со встроенной проверкой, другой с возвращением NULL наружу, чтобы в вызывающем коде было явно видно какой из них использован в данном месте.

В случае демона могут быть разные варианты, где-то можно и уронить процесс, где-то не надо. При ронянии процесса следует учитывать: если процесс сожрал всю память, то после его перезапуска (если он случится после падения) велика вероятность что следующая итерация тоже сожрёт всю память по той же внешней причине что и текущая, и опять упадёт. В целом, такая работа сомнительна. А вот если демон как-то обрабатывает нехватку памяти, то он сможет работать и без падений. Например, он может запускать при этом удаление из памяти малоприоритетных данных. Хотя такую логику лучше тоже в аллокатор встраивать, но всякие случаи бывают.

firkax ★★★★★
()
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария