Використання молока в PIC


10

Як я можу використовувати malloc()та free()функціонувати в PIC? Я перевірив stdlib.hзаголовок і про них не згадується. Я використовую MCC18.

Хтось мав ними користуватися?

Мені вони потрібні, оскільки я переношу бібліотеку з Windows XP в PIC. Посібник з перенесення говорить до

адаптувати конкретні функції Операційної системи до моїх функцій PIC

Але я не знаю, як "перекласти" функції malloc()та free()функції.


4
Спробуйте використовувати статичний розподіл, якщо це можливо.
Нік Т

1
Чому? Проблема справді полягає в тому, що я пишу нижній шар (конкретний для платформи) досить величезної бібліотеки і безлічі функцій, я не маю уявлення, для чого вони користуються. Я не знаю, як змінити динамічний до статичного ..
stef

11
Здається, що мікроконтролер PIC з <4 КБ оперативної пам’яті може бути невірним для вашої програми. На ПК вимірюйте об'єм пам'яті бібліотеки ПК перед початком порту. Можливо, вам буде краще з чимось улюбленцем типу ARM Cortex-M3. Правило: якщо база даних коду, яку ви переносите, занадто велика, щоб зрозуміти, вона не впишеться в PIC.
Тобі Джаффі

Драйвери Windows (і програми взагалі) по суті написані з парадигмою "необмеженої оперативної пам'яті", оскільки якщо фізична ОЗУ вичерпана, віртуальна пам'ять може бути замінена. Залежно від того, що робить бібліотека, вона може спожити більше, ніж 4 КБ. доступний у вашому PIC18F87J11. Я підозрюю, що ви не зможете оцінити, скільки пам'яті збирається використовувати драйвер.
Адам Лоуренс

Інша потенційна проблема: Int32 - це 32 біти, тоді як для компілятора MCC18 - це лише 16 біт. Ви можете отримати дивні проблеми з переповненням, якщо не будете обережні.
Адам Лоуренс

Відповіді:


8

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

char * next_alloc;
void * malloc (розмір int)
{
    char * this_alloc;
    this_alloc = next_alloc;
    якщо ((END_OF_ALLOC_SPACE - this_alloc) <розмір)
      повернення -1;
    next_alloc + = розмір;
    повернути this_alloc;
}
void free (void * ptr)
{
    if (ptr)
        next_alloc = (char *) ptr;
}

Приємно і просто, і всього два байти загальних накладних витрат на будь-яку кількість виділень. Виклик free () на блоці буде розміщувати цей блок і все після нього.

Трохи складнішими схемами розподілу можна керуватись за допомогою двох покажчиків - одного, який виділяє матеріал з нижньої частини пам’яті, що рухається вгору, а один з них йде зверху пам’яті вниз. Можливо також використовувати компактний збірник сміття, якщо дані в купі є однорідними і знається, де всі зовнішні посилання на нього.


Щоб бути поінформованим про можливості, читайте відповідь на електроніці.stackexchange.com
questions/7850/…

14

malloc()в мікроконтролерах взагалі вважається «поганою справою». Але якщо вам це абсолютно потрібно, ви захочете знайти сторонні версії.

Якщо вам пощастить, код, який ви переносите, може не покладатися на повторне використання блоків пам'яті. Якщо це так, ви можете написати простий розподільник, який повертає вказівник у буфер ОЗУ, а потім переміщує вказівник на запитуваний розмір блоку.

Я успішно використовував цей підхід до перенесення бібліотек ПК на мікроконтролери.

Нижче ви встановите алокатор за допомогою my_malloc_init()і розподілити пам'ять за допомогою my_malloc(). my_free()є, щоб задовольнити залежність, але насправді нічого не зробить. Врешті-решт, у вас просто не вистачить місця.

Щоб зробити цю роботу, вам потрібно буде виміряти найгірший обсяг пам'яті свого коду (зробіть це на ПК, якщо можливо), а потім налаштувати HEAP_SIZEвідповідним чином. Перш ніж зайти до тієї частини вашої бібліотеки, яка потребує динамічної пам'яті, зателефонуйте my_malloc_init(). Перед повторним використанням переконайтесь, що все ще нічого не вказує heap.

uint8_t heap[HEAP_SIZE];
uint8_t *heap_ptr;

void my_malloc_init(void)
{
    heap_ptr = heap;
}

void *my_malloc(size_t len)
{
    uint8_t *p = heap_ptr;
    heap_ptr += len;
    if (heap_ptr >= heap + HEAP_SIZE)
        return NULL;
    else
        return p;
}

void my_free(void)
{
    // do nothing
}

(зверніть увагу: у реальному світі вам може знадобитися розглянути вирівнювання вказівника, тобто округлення heap_ptrна 2 або 4 байти)

Інший варіант - використовувати більш просту структуру розподілу, ніж malloc()зазвичай надає, як FreeList , хоча це може не дозволяти виділяти блоки змінного розміру.


3
Цікаво, коли malloc IS вважається хорошою справою у вбудованих.
Kellenjb

1
Я все ще погоджуюся, що ви не хочете динамічного розподілу в програмах, як говорили інші, але це чудовий спосіб зробити це. Сторонній лоток, призначений для вбудовування, на сьогоднішній день є найкращим вибором. Уникнення сегментації є обов'язковим. @jobyTaffey Добре написано.
Кортук

1
@Kellenjb добре, це зовсім нове запитання :-)
Toby Jaffey

1
Я б сказав, що my_free повинен встановити heap_ptr на передане значення, фактично звільнивши вказаний елемент і все, що виділяється після нього. Очевидно, що потрібно розподіляти речі в такій послідовності, яка дозволяє таке використання, але такі зразки не є рідкістю. Іншим корисним варіантом є наявність двох пар функцій allo / free, одна з яких виділяє зверху вниз, а друга виділяє знизу вгору.
supercat

13

Це навряд чи є відповіддю на ваше запитання, але динамічне розподіл пам’яті, як правило, спокушається на невеликих середовищах оперативної пам’яті та за відсутності операційної системи (наприклад, у світі мікроконтролерів)… типовий купольний простір, який ви маєте у вбудованому середовищі, як правило вимірюється сотнями байт ...

Реалізація malloc та free - це, по суті, підтримка пов'язаного списку структур "вільного сегмента", і як ви можете собі уявити, метадані, пов'язані з вільними сегментами, не є істотними порівняно з типово наявним об'ємом пам'яті ... тобто "накладними" "управління динамічним пулом пам'яті вимагає значної кількості доступних ресурсів.


Існують реалізації, де метаданих дуже мало. Для виділеного блоку вам потрібен лише його розмір. Для невикористаних блоків вказівник (і) зв'язаного списку, як правило, може підходити безкоштовно, навіть при досить розумних мінімальних розмірах блоків.
Відновіть Моніку

Проблема з невеликими, довго працюючими системами, що використовують мікроконтролери, полягає не у метаданих, а у фрагментації пам'яті. Що ще гірше, невеликі зміни у вашому коді можуть внести фрагментацію пам’яті там, де раніше її не було, так що ви можете зробити невинно виглядаючі зміни, що раптом змусить вашу програму перестати працювати «занадто рано».
Відновіть Моніку

11

Я не знаю , якщо С18 стандартної бібліотеки підтримує mallocі free, але Microchip App Note AN914 показує , як можна реалізувати свої власні.

У будь-якому випадку, Томас та інші плакати пропонували використовувати динамічну пам’ять на PIC з їх дуже невеликим простором оперативної пам’яті загрожує небезпекою. Ви можете швидко вичерпати суміжний простір через відсутність більш досконалих менеджерів віртуальної пам’яті, які дають вам повноцінні операційні системи, що призводить до невдалого розподілу та збоїв. Гірше, що це може бути не детермінованим, і, швидше за все, це буде біль налагодження.

Якщо те, що ви робите, по-справжньому динамічно визначається під час виконання (рідкісне для більшості вбудованих речей), і вам потрібно виділити простір лише на пару дуже особливих випадків, я міг би бачити mallocі freeбути прийнятним.


Вичерпання суміжного простору, відомий також як фрагмент купи - це проблема, яка повністю не залежить від того, наскільки великий ваш адресний простір і чи є у вас віртуальна пам'ять. Можливо, ви захочете торгувати деякою витраченою оперативною пам’яттю для меншої фрагментації купи, але, зрештою, у довготривалій системі у вас немає гарантій щодо того, що не вичерпається купольний простір. Єдина відмінність між маленькими та великими системами тут полягає в тому, скільки часу потрібно, щоб диск почав обмолочувати (у системах з VM, що завантажений на диск), або алокатор повертав NULL (на вбудованих матеріалах).
Відновіть Моніку

@KubaOber: Зазвичай можна гарантувати, що оперативна пам’ять певного розміру зможе обробляти будь-яку послідовність операцій розподілу та звільнення, яким ніколи не потрібно одночасно виділяти більше (менший) об'єм оперативної пам'яті. Проблема з вбудованими системами полягає в тому, що для гарантування успіху навіть у гіршому випадку буде потрібно набагато більше оперативної пам’яті, ніж було б потрібно без фрагментації.
supercat

@supercat Ви маєте рацію. Мені справді було драматично драматично. Є офіційні докази цих гарантій.
Відновіть Моніку

2

Ну, наскільки великий ваш ПОС щодо пам’яті?

malloc - це дуже неефективний спосіб розподілу пам’яті. Проблема з цим полягає в тому, що пам'ять може роздроблено за допомогою частого звільнення та зловмисних програм, і лише кілька кілобайт пам'яті, помилки розподілу є занадто поширеними. Цілком ймовірно, що якщо ви використовуєте менший чіп або більш ранній PIC18, немає підтримки для malloc, оскільки Microchip або вважав це дуже важким для реалізації (або, можливо, навіть неможливим у деяких випадках), або недостатньо використовувався для того, щоб бути того варто Не кажучи вже про це, але це також досить повільно, ви дивитесь на 1 цикл, щоб використовувати статичний буфер, який уже доступний, і 100s на 1000s циклів, щоб зробити malloc.

Якщо ви хочете виділити статично, створіть такі речі, як буфер для своїх функцій sprintf (якщо такий є, близько 128 байт), буфер для вашої SD-карти (якщо такий є) тощо, поки не усунете необхідність у malloc. В ідеалі ви використовуєте його лише там, де вам це абсолютно потрібно, і не можете уникнути статичного розподілу, але такі ситуації, як правило, рідкісні і, можливо, є ознакою того, що вам слід дивитись на більші / потужніші мікроконтролери.

І якщо ви розробляєте / переносите "операційну систему" на PIC18 і якщо вона підтримує мікроконтролери, вона, ймовірно, має підтримку статичного розподілу. Наприклад, SQLite3 підтримує статичний розподіл - ви виділяєте йому великий масив буфера, і він використовує це, де це можливо, навіть якщо це не для мікроконтролерів. Якщо це не так, то ви впевнені, що він призначений для невеликого PIC18?


Я бачу, що ви маєте на увазі .. Я використовую PIC18F87J11, у якого є 128K оперативної пам’яті, це може бути достатньо?
stef

Стефано, цей чіп має 3 904 байт оперативної пам’яті. Він має 128 Кб пам'яті програми.
W5VO

@Stefao Salati - 3.8KB крихітний.
Томас О

Правильно вибачте ... ви думаєте, це все одно може бути достатньо?
stef

@Stefano Salati, вибачатися не потрібно. Я думаю, ти б насправді наполягав на цьому. Це може спрацювати, але це займе шматок продуктивності та вільної пам’яті.
Томас О

2

Якщо ви розглядаєте malloc()і free()для програмно-апаратних засобів , я пропоную вам поглянути на УНЦ / OS-II і OSMemGet()і OSMemPut(). Хоча malloc()ви дозволяєте виділити довільний блок пам'яті, OSMem*()надайте вам блок фіксованого розміру з попередньо виділеного пулу. Я вважаю такий підхід хорошим балансом між гнучкістю malloc()та надійністю статичного розподілу.


0

AFAIK, щоб зробити це правильно, вам справді потрібно дивитись на пристрій з якимось блоком управління пам’яттю (MMU). Хоча існують динамічні механізми розподілу для серії PIC18, вони насправді не будуть такими надійними - і, говорячи як хтось, хто працював над прошивкою, що розсуває межі серії PIC18, я можу сказати, що ви не збираєтеся отримувати велика програма там, якщо ви витратите всі накладні витрати на диспетчер пам'яті.

Краще рішення: спробуйте зрозуміти, що це робить, і чому йому потрібно динамічне розподілення. Перевірте, чи не можете ви перефактувати його для роботи зі статичним розподілом. (Може статися так, що це просто неможливо - якщо бібліотека / додаток призначені для того, щоб робити щось, що вільно масштабується, або не має меж кількості вкладених даних, які він може прийняти.) Але іноді, якщо ви дійсно думаєте про те, що ви намагаєтеся зробити, ви можете виявити, що можна (а можливо, навіть досить просто) використовувати замість нього статичне виділення.


1
Ви невірні. MMU дозволяє взаємодіяти із зовнішньою пам’яттю (ймовірно, більше, ніж 4 кБ на PIC). Динамічна та статична розподіл з ММУ та без нього є дуже малою. Після того, як ви почнете потрапляти у віртуальну пам'ять, є різниця, але це лише дотично пов'язане з malloc.
Кевін Вермер

1
Ранні програмісти Macintosh досить часто використовували malloc () та free () (або їх еквіваленти Pascal), незважаючи на те, що на ранніх комп'ютерах Macintosh не було MMU. Думка, що "правильно" за допомогою malloc () вимагає MMU, здається мені неправильною.
davidcary
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.