Здравствуйте, Marty, Вы писали:
vsb>>2. Компилятор кладёт этот буфер в секцию `.bss`.
M>Я не помню, что такое .bss. Есть секции для инициализированных переменных (и вроде не обязательно нулями), и для не инициализированных. Для последних вроде никто обнуление не делает
Нет секции для не инициализированных (если самому её не создать).
M>Не совсем понял, в чем отличие стартап кода и _start из libc?
стартап запускается при старте, настраивает тактирование, копирует секцию _data из флеша в срам и запускает _start.
M>Я вообще не уверен, что в прошивке для МК есть какие-то секции.
В неявном виде есть.
vsb>>6. Наш буфер вообще никак не использует этот факт своего обнуления.
vsb>>Очень забавно за всем этим наблюдать, конечно.
M>Не наблюдай. Не инициализируй переменные там, где это не нужно. Перепиши стартап.
Здравствуйте, vsb, Вы писали:
M>>Я не помню, что такое .bss. Есть секции для инициализированных переменных (и вроде не обязательно нулями), и для не инициализированных. Для последних вроде никто обнуление не делает
vsb>Нет секции для не инициализированных (если самому её не создать).
Полистал гугл, он говорит, что .data — сегмент инициализированных данных, .bss — сегмент неинициализированных данных. Ты что-то путаешь, похоже
M>>Не совсем понял, в чем отличие стартап кода и _start из libc?
vsb>стартап запускается при старте, настраивает тактирование, копирует секцию _data из флеша в срам и запускает _start.
Ну, вообще-то не совсем так, у тебя какая-то каша в голове.
Я уже давно не брал в руки шашки, могу конечно, тоже ошибаться.
Изначально там есть какой-то "бутлоадер", который присовывает компилятор/линкер твоей системы разработки, который распаковывает инициализированные данные из прошивки в ОЗУ. Всё. Больше он ничего не делает. Да, этот бутлоадер, возможно, обнуляет неинициализированные глобальные переменные. Но с тем же успехом он может ничего не делать, или забивать неинициализированные данные значением 0xDEADBEAF, например.
Этот "бутлоадер" — примерный аналог загрузчика ОС, который загружает твой EXE в систему с диска. И, полагаю, его тоже можно подменить, и не думаю, что это очень сложно.
Далее, если говорить о сишечке, то управление просто передаётся в твой main. Для плюсов есть точка входа до main, в которой вызываются конструкторы глобальных объектов, и уже из которой управление передаётся в main.
Хотя, конечно, где-то может и не совсем так. На STM32, с которыми я имел дело, там до main исполнялся код, который вызывал настройку тактирования. Для STM32 я имел дело с Keil и GCC. Там этот стартап написан на асме (у кейла и гцц — разные диалекты), и его без проблем можно поправить ручками, например, задать размер кучи (я переделал, чтобы можно было дефайнами задавать без правки стартапа). Там я не припоминаю никаких обнулений. Кстати, настройку тактирования я сам писал для STM32F1/3/4, мне хотелось, чтобы частоту кристалла и частоту кварца можно было просто задать define'ами в проекте. Честно говоря, я уж и не помню, как оно до меня работало, но там какой-то гемор был с переносом на другие чипы-кварцы, что у нас случалось регулярно, и поэтому тактирование я и переписал.
M>>Не наблюдай. Не инициализируй переменные там, где это не нужно. Перепиши стартап.
vsb>Ты ещё предложи компилятор С переписать.
Компилятор — нет. А вот стартапы твоей железяки полезно знать. И залезть в них и что-то переписать ни разу не рокет саенс.
Да, пока писал, немножко вспомнил. У нас использовались стартапы от CMSIS вроде, изрядно переделанные под себя.
Кстати, а с чего ты взял, что там на старте всё по десять раз нулями перезаписывается?
Здравствуйте, vsb, Вы писали:
vsb>1. Язык С гарантирует, что все глобальные переменные инициализируются нулями. vsb>2. Компилятор кладёт этот буфер в секцию `.bss`. vsb>3. Стартап код на ассемблере обнуляет секцию `.bss` сразу после запуска контроллера. vsb>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности). vsb>5. Итого наш хиленький процессорчик после подачи питания просто так потратил порядка сотни тысяч тактов, гоняя нули.
Насколько я знаю, в Аде это поведение настраивается. Можно не инициализировать.
M>>>Я думал, что чтобы произошла инициализация нулями, надо написать что-то типа M>>>
byte buf[1024] = { 0 };
CRT>>здесь ты явно инициализировал нулем элемент с нулевым индексом
M>Ну, плюсики вроде бы при этом весь массив обнуляют, хз как сишечка
наврал я.
Ну то есть да, если ты написал byte buf[1024] = { 0 } то это явная инициализация только элемента с нулевым индексом. Однако если у тебя указана инициализация хотя бы одного элемента, то остальные по умолчанию обнуляются. Поэтому твоя конструкция обнулит весь массив. Это и для С и для С++
Здравствуйте, vsb, Вы писали:
vsb>3. Стартап код на ассемблере обнуляет секцию `.bss` сразу после запуска контроллера. vsb>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности).
Достоинства C/C++ в том, что в них зависимость от стартапа и вообще CRT достаточно прозрачна — большинство конструкций языка порождает только голый код, и лишь небольшая их часть зависит от функций/переменных CRT/стартапа. Функции, которая не использует этих зависимых конструкций, для работы требуется только настроенный стек (и то не всегда). Если все это контролировать, нетрудно писать программы, которым не нужны ни CRT, ни стартап — вход может быть в любую функцию, не только в main.
Если Вы используете "канонический" C/C++ со стандартным стартапом и автоматическим входом через main, и при этом изменить стандартное поведение, то нужно управлять размещением и режимами работы вручную (секции, прагмы, ключи компилятора/линкера и т.п.).
У меня под винду уже очень давно свой стартап для C++, написанный целиком на C++, без единой строчки ассемблерного кода. Если бы не требовалось вызывать конструкторы статических объектов и регистрировать их деструкторы, можно было бы обойтись и вовсе без стартапа. А для 8-разрядных AVR, если писать на C++ без статических объектов классов, имеющих конструкторы, даже стартап не линкуется, насколько я помню.
Здравствуйте, vsb, Вы писали:
vsb>это уже не C. В C нет никаких секций.
В C также нет ни "процессора" или "микроконтроллера", ни "тактов". Тот C, который "гарантирует" — это спецификация абстрактной машины, не более того. Она позволяет Вам писать на C даже для "хилого микроконтроллера", и программа будет как-то работать. Желаете более эффективной работы — придется настраивать реализацию, это очевидно.
Здравствуйте, Евгений Музыченко, Вы писали:
vsb>>это уже не C. В C нет никаких секций.
ЕМ>В C также нет ни "процессора" или "микроконтроллера", ни "тактов". Тот C, который "гарантирует" — это спецификация абстрактной машины, не более того. Она позволяет Вам писать на C даже для "хилого микроконтроллера", и программа будет как-то работать. Желаете более эффективной работы — придется настраивать реализацию, это очевидно.
Но в С есть требование того, чтобы глобальные переменные были обнулены. И от этого никуда не уйдёшь. Если это требование игнорировать, то это уже не С.
А реализация этого требования бесплатно не бывает. Кто бы что про операционную систему не говорил, память сама собой не обнулится.
И это кардинально противоречит философии языка, на мой взгляд.
Здравствуйте, vsb, Вы писали:
vsb>Но в С есть требование того, чтобы глобальные переменные были обнулены.
Это не требование, а утверждение. Это для программиста, который может рассчитывать, что в стандартной реализации они будут обнулены. Самому же C глубоко фиолетово, что будет в тех переменных.
vsb>Если это требование игнорировать, то это уже не С.
Во-первых, это классическая ситуация из серии "шашечки или ехать". Если нужно "все как в стандарте", то забудьте про скорость и объем памяти, в C об этом тоже ничего не говорится. Гарантируется лишь правильный результат исполнения программы в соответствии со стандартом.
Во-вторых, C в микроконтроллере, ядре ОС и подобных местах — это по определению "уже не C". Что там в argv и argc, что в environ, куда идет результат, возвращаемый main, куда печатает printf, что делают _spawn, _exec и подобные функции стандартной библиотеки?
vsb>это кардинально противоречит философии языка, на мой взгляд.
Вы делаете программы на языке, или таки на его философии? Чем философия мешает делать программы с любым желаемым поведением?
Здравствуйте, vsb, Вы писали:
vsb>Но в С есть требование того, чтобы глобальные переменные были обнулены. И от этого никуда не уйдёшь. Если это требование игнорировать, то это уже не С.
1. Нафига вам вообще понадобились глобальные переменные.
2. В микроконтроллере никто не мешает разместить переменные самому в допустимы диапазон SRAM
struct AppGlobals {
int x,y;
};
#define GLOBAL ((struct AppGlobals*)APP_GLOBALS_ADDRESS)
...
GLOBAL->x=47;
3. вы можете объяснить линковщику что вам надо (у него есть скрипт для этого) и ваш код инициализации может ничего не обнулять и даже не копировать если это вам не требуется.
Здравствуйте, vsb, Вы писали:
vsb>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности). vsb>5. Итого наш хиленький процессорчик после подачи питания просто так потратил порядка сотни тысяч тактов, гоняя нули.
В контроллерах доступную память зачастую распределяют "в уме", т.е. просто определяют константы адерсов этой памяти, и ничего нулями забивать не нужно.
Здравствуйте, vsb, Вы писали:
SIG>>Если ты так в embedded напишешь, то линкер эти твои нули засунет в ПЗУ, занимая там место, а стартап-код будет заботливо копировать в ОЗУ. Возможно дважды, как в STM32
vsb>Это понятно но это текущая кривая реализация. Ничего не мешает такие переменные сувать в bss, а неинициализированные ещё куда.
В bss как раз неинициализированные суются. Ты, кстати, под STMку в чем разрабатываешь?
Здравствуйте, vsb, Вы писали:
vsb>Но в С есть требование того, чтобы глобальные переменные были обнулены. И от этого никуда не уйдёшь. Если это требование игнорировать, то это уже не С.
Разве?
vsb>А реализация этого требования бесплатно не бывает. Кто бы что про операционную систему не говорил, память сама собой не обнулится.
Это копейки в любом случае
vsb>И это кардинально противоречит философии языка, на мой взгляд.
Здравствуйте, vdimas, Вы писали:
V>В контроллерах доступную память зачастую распределяют "в уме", т.е. просто определяют константы адерсов этой памяти, и ничего нулями забивать не нужно. V>
Здравствуйте, Marty, Вы писали:
ЕМ>>Во-вторых, C в микроконтроллере, ядре ОС и подобных местах — это по определению "уже не C".
M>С чего бы не C?
С того, что это не полноценный C, состав которого включает ядро языка, стандартную библиотеку и стандартное же окружение. Это подмножество, куда даже ядро может входить не полностью (например, без плавучки), а уж библиотека чаще всего прилично урезана. Соответственно, там может не скомпилироваться или не заработать полностью корректная, платформенно-независимая программа (скажем, POSIX-совместимая), даже если для нее достаточно памяти.
M>От этого C не перестаёт быть C
Если говорить о "самом языке" (его ядре), то да. Но той же инициализацией статических переменных занимается не оно.
Здравствуйте, Marty, Вы писали:
SIG>>>Если ты так в embedded напишешь, то линкер эти твои нули засунет в ПЗУ, занимая там место, а стартап-код будет заботливо копировать в ОЗУ. Возможно дважды, как в STM32
vsb>>Это понятно но это текущая кривая реализация. Ничего не мешает такие переменные сувать в bss, а неинициализированные ещё куда.
M>В bss как раз неинициализированные суются. Ты, кстати, под STMку в чем разрабатываешь?
Блин, сто раз уже написали — нет в С неинициализированных глобальных переменных. Только локальные. bss обнуляется на старте, точка. Ты вроде в С спец, должен это лучше меня знать. Можно удалить из стартапа этот код обнуления, можно не вызывать libc _start, тогда не будет обнуляться, но это уже не соответствует тому, что требует стандарт.
Под vscode. Не совсем под STM, под её китайский перепев (GD32).
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>>>Во-вторых, C в микроконтроллере, ядре ОС и подобных местах — это по определению "уже не C".
M>>С чего бы не C?
ЕМ>С того, что это не полноценный C, состав которого включает ядро языка, стандартную библиотеку и стандартное же окружение. Это подмножество, куда даже ядро может входить не полностью (например, без плавучки), а уж библиотека чаще всего прилично урезана. Соответственно, там может не скомпилироваться или не заработать полностью корректная, платформенно-независимая программа (скажем, POSIX-совместимая), даже если для нее достаточно памяти.
А при чём тут POSIX и C? Это как бы разные вещи. Не, я согласен, у меня printf тоже не линкуется, ему там ещё с десяток вызовов нужно реализовать, но в целом ничего там не урезано, что не относится к вводу-выводу, обычная libc (newlib). Плавающую точку gcc умеет компилировать софтово (с производительностью, полагаю, будет крайне грустно, но работать будет).
Здравствуйте, vsb, Вы писали:
vsb>А при чём тут POSIX и C? Это как бы разные вещи.
Вы таки или крестик снимите, или трусы наденьте. То у Вас "в C нет никаких секций, это уже не C", то "при чем тут POSIX?". Есть спецификация языка, есть стандарты, которые соблюдаются в канонических "пользовательских" реализациях, но почти никогда не соблюдаются в ограниченных "системных". Если это "все-таки C", то и "C с секциями" — "тоже C".
Вообще, любой серьезный язык только выигрывает от наличия возможностей управления генерацией кода, режимами связывания, размещением кода/данных в памяти и прочими платформенно-зависимыми вещами. Даже строгий Pascal во многих реализациях такое имеет, а уж C/C++ сам бог велел все это иметь, и желательно побольше.
Здравствуйте, vsb, Вы писали:
vsb>Можно удалить из стартапа этот код обнуления, можно не вызывать libc _start, тогда не будет обнуляться, но это уже не соответствует тому, что требует стандарт.
Здравствуйте, Marty, Вы писали:
M>Это для работы с периферией, она там обычно прикидывается памятью. Для обычного кода зачем что-то распределять в уме?
Чтобы уменьшить размер образа, оставив в нём только код.