C стандартні бібліотеки з голого металу


24

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

Однак для голого металу немає базової ОС. Чи є стандарт, пов’язаний із тим, як слід реалізувати бібліотеку змінного струму, чи потрібно перевчити особливості реалізації бібліотеки, коли ви переходите на нову плату, яка забезпечує інший BSP?


4
Неправильний сайт для вашого запитання.
ott--

8
Я голосую, щоб закрити це питання поза темою, оскільки воно належить до Stack Overflow .
uint128_t

1
Взагалі ви обійдетесь без. Навіщо тобі потрібні такі речі без операційної системи для їх підтримки? memcpy і таке точно. Файлові системи, необов'язково, хоча реалізовані fopen, close тощо, є тривіальним, наприклад, проти оперативної пам'яті. printf () дуже дуже важкий, потрібні тонни та тонни коду, без цього. будь-які заміни вводу / виводу або без них. newlib досить екстремальний, але чи допомагає, якщо ви не можете обійтися, але вам доведеться впровадити систему на бекенді, так що вам потрібен додатковий шар?
old_timer

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

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

Відповіді:


20

Так, є стандарт, просто стандартна бібліотека C . Функції бібліотеки не потребують "повноцінної" ОС або будь-якої ОС взагалі, і там є ряд реалізацій, пристосованих до "голого металу", 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 вашого обладнання; потім шари Ньюліба printfповерх них. Файловий ввод / вивід аналогічно покладається на кілька примітивів.
Брайан Драммонд

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

2
Ньюліб також величезний, якщо ви маєте справу з MCU з 1 або 2 кБ простору коду ...
Брайан Драммонд

2
@dwelch Ви не створюєте власну ОС, ви створюєте бібліотеку C. Якщо ви цього не хочете, то так, це зайве велике.
труба

8

Чи є стандарт, пов’язаний із тим, як слід реалізувати бібліотеку змінного струму, чи потрібно перевчити особливості реалізації бібліотеки, коли ви переходите на нову плату, яка забезпечує інший BSP?

По-перше, стандарт 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__...

Прикладів є більше, але я зараз не маю їх під рукою.

Решта бібліотеки можна реалізувати без сторонньої допомоги. (*)


(*) Caveat: Реалізація PDCLib ще не завершена, тому я, можливо, переглянув річ чи дві. ;-)


4

Стандарт C фактично визначається окремо від робочого середовища. Не передбачається присутність хост-операційної системи, і ті частини, які залежать від хоста, визначаються як такі.

Тобто C Standard - це вже досить голий метал.

Звичайно, ті мовні частини, які ми так любимо, бібліотеки, часто там, де основна мова підштовхує хостинг конкретних речей. Отже, типові матеріали для компілятора "xxx-lib" знайдені для багатьох інструментів платформи для голих металів.


3

Приклад мінімальної експлуатації Newlib

Тут я надаю високо автоматизований і задокументований приклад, який показує новоліб в дії в QEMU .

За допомогою newlib ви реалізуєте власні системні виклики для своєї бареметальної платформи.

Наприклад, на наведеному вище прикладі ми маємо приклад програми exit.c:

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

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

і в окремому файлі C common.c, ми будемо виконувати exitз ARM semihosting :

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 ()?
supercat

Не знаю. Я думаю, ваша суть - це буфер, який він уже має, чи не так? Але навіть printf не потребуватиме malloc. Можливо, динамічно розподілити деякі внутрішні змінні, коли обчислення запитуваного виходу важче, ніж передбачення складеного розподілу (динамічні змінні функції)? Я впевнений, що для спринту потрібен malloc (arm-none-eabi-newlib). Зараз я експериментував з простою програмою, що використовує sprintf на комп'ютері (glibc). Це ніколи не називали malloc. Потім використовується printf. Це називається malloc. Маллок був фальшивим, завжди повертався 0. Але він працював чудово. Вони повинні були надрукувати рядок і десяткову змінну. @supercat
Айхан

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