C стандартные библиотеки на голом металле


24

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

Однако для голого металла ОС не существует. Существует ли какой-либо стандарт, связанный с тем, как должна быть реализована библиотека ac, или вам приходится заново изучать особенности реализации библиотек, когда вы переключаетесь на новую плату, которая обеспечивает другой BSP?


4
Неправильный сайт по вашему вопросу.
ot--

8
Я голосую, чтобы закрыть этот вопрос как не по теме, потому что он относится к переполнению стека .
uint128_t

1
Как правило, вы обходитесь без. Зачем вам такие вещи без операционной системы для их поддержки? memcpy и такие уверены. Файловые системы, не обязательно, хотя реализованы fopen, close и т. Д., Тривиальны для ram, например. printf () очень-очень-очень тяжелый, требуются тонны и тонны кода, обходитесь без. любой ввод / вывод заменить или обойтись без. newlib довольно экстремален, но помогает, если вы не можете обойтись без него, но вы все равно должны внедрить систему на бэкэнд, так вам нужен дополнительный слой?
old_timer

12
Хотя этот вопрос касается программного обеспечения, он очень специфичен для встроенного программирования, которое обычно отвергается SO. Поскольку у нас уже есть несколько хороших ответов, миграция не подходит.
Дэйв Твид

1
Хотя newlib упоминается ниже в ответе, вы также можете найти полезной newlib-nano - она ​​предназначена для упрощенной версии для использования во встроенных системах с ограниченными ресурсами. Я использую его в проектах на микроконтроллерах Cortex M0. Ряд компиляторов (Atollic TrueSTUDIO - один) даст возможность использовать newlib или newlib-nano.
jjmilburn

Ответы:


20

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

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

Если вы используете linux (возможно, также OSX и, возможно, даже cygwin / msys?) И тип man strlen, у него должен быть раздел, называемый чем-то вроде CONFORMING TO, который скажет вам, что реализация соответствует определенному стандарту. Таким образом, вы можете выяснить, является ли то, что вы используете, стандартной функцией или зависит от конкретной ОС.


1
мне любопытно, как stdlibреализует, stdioне будучи зависимым от ОС. как fopen(), fclose(), fread(), fwrite(), putc()и getc()? а как malloc()работает без общения с ОС?
Роберт Бристоу-Джонсон

4
В Newlib есть слой под ним, называемый libgloss, который содержит (или вы пишете) пару десятков функций для вашей платформы. Например, a getcharи putcharкоторые знают о UART вашего оборудования; затем слои Newlib printfповерх них. Файловый ввод / вывод также будет опираться на несколько примитивов.
Брайан Драммонд

да, я не читал внимательно второй абзац трубы. Помимо работы с stdinи stdoutи stderr (которая заботится о putchar()и getchar()), которая направляет ввод / вывод из / в UART, если на вашей платформе есть файловое хранилище, как, например, с флеш-памятью, вы должны также написать для этого клей. и вы должны иметь средства для malloc()и free(). Я думаю, что если вы позаботитесь об этих проблемах, вы можете в значительной степени запустить портативный C во встроенной цели (нет, argvни argc).
Роберт Бристоу-Джонсон

2
Newlib также огромный , если вы имеете дело с MCU с 1 или 2 килобайта кодового пространства ...
Брайан Драммонд

2
@dwelch Вы создаете не собственную ОС, а библиотеку Си. Если ты не хочешь этого, тогда да, это не нужно, большой.
труба

8

Существует ли какой-либо стандарт, связанный с тем, как должна быть реализована библиотека ac, или вам приходится заново изучать особенности реализации библиотек, когда вы переключаетесь на новую плату, которая обеспечивает другой BSP?

Во-первых, стандарт C определяет нечто, называемое «автономной» реализацией, в отличие от «размещенной» реализации (с которой большинство из нас знакомо - полный спектр функций C, поддерживаемых базовой ОС).

«Отдельно стоящая» реализация должна определять только подмножество заголовков библиотеки C, а именно те, которые не требуют поддержки, или даже определения функций (они просто выполняют #defines и typedefs):

  • <float.h>
  • <iso646.h>
  • <limits.h>
  • <stdalign.h>
  • <stdarg.h>
  • <stdbool.h>
  • <stddef.h>
  • <stdint.h>
  • <stdnoreturn.h>

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

  • getenv()( extern char * * environ)
  • system()( fork()/ execve()/ wait())
  • malloc()и free()( brk()/ sbrk())
  • _Exit()( _exit())
  • time() (еще не реализовано)

И для <stdio.h>(возможно, наиболее "вовлеченного в ОС" из заголовков C99):

  • какой-то способ открыть файл ( open())
  • какой-то способ закрыть его ( close())
  • какой-то способ удалить его ( unlink())
  • какой-то способ переименовать его ( link()/ unlink())
  • какой-то способ написать в него ( write())
  • какой-то способ читать из него ( read())
  • какой-то способ изменить положение внутри него ( lseek())

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

  • По time()закону функция может просто вернуться, (time_t)-1если нет доступных механизмов учета времени.

  • Описанные обработчики сигналов <signal.h>не должны вызываться ничем, кроме вызова raise(), нет требования, чтобы система фактически отправляла что-то подобное SIGSEGVв приложение.

  • Заголовок C11 <threads.h>, который (по очевидным причинам) очень зависит от ОС, вообще не требуется указывать, если реализация определяет __STDC_NO_THREADS__...

Есть еще примеры, но у меня их сейчас нет под рукой.

Остальная часть библиотеки может быть реализована без какой-либо помощи со стороны среды. (*)


(*) Предостережение: реализация PDCLib еще не завершена, поэтому я мог пропустить одну или две вещи. ;-)


4

Стандарт C фактически определяется отдельно от операционной среды. Не делается никаких предположений о наличии хост-ОС, и те части, которые зависят от хоста, определяются как таковые.

То есть C Standard уже довольно голый металл.

Конечно, те языковые части, которые мы так любим, библиотеки, часто находятся там, где основной язык подталкивает к размещению конкретных вещей. Следовательно, типичный материал для кросс-компиляции "xxx-lib" найден для многих инструментов на платформе.


3

Пример минимального запуска Newlib

Здесь я приведу высоко автоматизированный и документированный пример, который показывает newlib в действии в QEMU .

С newlib вы реализуете свои собственные системные вызовы для вашей платформы baremetal.

Например, в приведенном выше примере у нас есть пример программы exit.c:

#include <stdio.h>
#include <stdlib.h>

void main(void) {
    exit(0);
}

и в отдельном C-файле common.cмы реализуем exitс полухостингом ARM :

void _exit(int status) {
    __asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456");
}

Другие типичные системные вызовы, которые вы реализуете:

  • writeвыводить результаты на хост. Это можно сделать с помощью:

    • больше полухостинга
    • аппаратное обеспечение UART
  • brkдля malloc.

    Легко на голом металле, так как нам не нужно заботиться о подкачке!

TODO Интересно, реально ли достичь выполнения системных вызовов с упреждающим планированием, не вдаваясь в полноценную RTOS, такую ​​как Zephyr или FreeRTOS .

Самое замечательное в Newlib - это то, что он реализует все не связанные с ОС вещи, подобные string.hвам, и позволяет вам реализовывать только заглушки ОС.

Кроме того, вам не нужно реализовывать все заглушки, а только те, которые вам понадобятся. Например, если ваша программа нужна только вам exit, вам не нужно предоставлять print.

В исходном дереве Newlib уже есть некоторые реализации, в том числе реализация ARM-полухостинга newlib/libc/sys/arm, но по большей части вы должны реализовать свою собственную. Это, однако, обеспечивает прочную основу для этой задачи.

Самый простой способ настроить Newlib - это создать собственный компилятор с помощью crosstool-NG, вам просто нужно сообщить ему, что вы хотите использовать Newlib в качестве библиотеки C. Моя установка обрабатывает это автоматически для вас с помощью этого скрипта , который использует конфиги newlib, представленные на crosstool_ng_config.

Я думаю, что C ++ также будет работать, но TODO протестирует его.


3
@ downvoters: пожалуйста, объясните, чтобы я мог учиться и улучшать информацию. Надеемся, что будущие читатели увидят ценность единственной вводной установки Newlib, доступной в Интернете, которая просто работает :-)
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

2

Когда вы используете его без использования металла, вы обнаруживаете некоторые неосуществленные зависимости и вынуждены их обрабатывать. Все эти зависимости связаны с настройкой внутренних устройств в соответствии с индивидуальностью вашей системы. Например, когда я пытался использовать sprintf (), который использует malloc () внутри. Malloc имеет символ функции "t_sbrk" в качестве ловушки в коде, который должен быть реализован пользователем для обеспечения аппаратных ограничений. Здесь я могу реализовать это или создать свой собственный malloc (), если я считаю, что мог бы сделать лучше для встроенного оборудования, в основном для других целей, а не только для sprintf.


Зачем sprintf нужен malloc ()?
суперкат

Я не знаю. Я думаю, что вы имеете в виду буфер, который у него уже есть, не так ли? Но даже printf не должен нуждаться в malloc. Может быть, динамически распределить некоторые внутренние переменные, когда вычисление запрошенного вывода тяжелее, чем предвидение суммированного выделения (динамические переменные функции)? Я уверен, что для sprintf требуется malloc (arm-none-eabi-newlib). Теперь я экспериментировал с простой программой, использующей sprintf на компьютере (glibc). Это никогда не называлось malloc. Затем использовал printf. Это называется malloc. Маллок был подделкой, всегда возвращал 0. Но все работало нормально. Они должны были напечатать строку и десятичную переменную. @supercat
Ayhan

Я сам сделал несколько версий printf или аналогичных методов, настроенных для поддержки форматов, используемых моими приложениями. Десятичный вывод требует наличия буфера, достаточно длинного для хранения максимально длинного числа, но в противном случае базовая подпрограмма принимает структуру, первый член которой является функцией вывода, которая принимает указатель на эту структуру вместе с данными для вывода. Такой дизайн позволяет добавлять варианты printf, которые выводятся на консоли в стиле curses, сокеты и т. Д. У меня никогда не было необходимости в "malloc" в таких вещах.
суперкат
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.