Küsimus:
Kas malloc () ja free () kasutamine on Arduinos tõesti halb mõte?
Cybergibbons
2014-03-09 14:00:25 UTC
view on stackexchange narkive permalink

malloc () ja free () kasutamine tundub Arduino maailmas üsna haruldane. Seda kasutatakse puhtas AVR C-s palju sagedamini, kuid siiski ettevaatlikult.

Kas tõesti on halb mõte kasutada malloc () ja free () kood> Arduinoga?

muidu saab mälu tõesti kiiresti otsa ja kui teate, kui palju mälu kasutate, võite selle ka staatiliselt eraldada
Ma ei tea, kas see on * halb *, kuid arvan, et seda ei kasutata, sest enamiku visandite jaoks pole RAM peaaegu kunagi otsa saanud ja see on lihtsalt välgu ja väärtuslike kellatsüklite raiskamine. Ärge unustage ka ulatust (kuigi ma ei tea, kas see ruum on endiselt kõigi muutujate jaoks eraldatud).
Nagu tavaliselt, on õige vastus "see sõltub". Te pole esitanud piisavalt teavet, et teada saada, kas dünaamiline jaotamine sobib teile.
Kaheksa vastused:
#1
+43
JRobert
2014-03-11 21:36:25 UTC
view on stackexchange narkive permalink

Minu üldreegel manustatud süsteemide jaoks on ainult malloc () suurte puhvrite loomine ja ainult üks kord programmi alguses, nt setup () . Häda tuleb siis, kui eraldate ja eraldate mälu. Pikas perspektiivis seansi ajal mälu killustub ja lõpuks eraldamine ebaõnnestub piisavalt suure vaba ala puudumise tõttu, kuigi kogu vaba mälu on päringu jaoks enam kui piisav.

(Ajalooline perspektiiv, jätke vahele, kui see ei huvita): olenevalt laadija juurutusest on käitamisaja jaotuse ja kompileerimisaja jaotuse (intialiseeritud globaalide) ainus eelis kuusnurga faili suurus. Kui sisseehitatud süsteeme ehitati nii, et arvutid, millel oli kogu mälu, oleksid riiulil, laaditi programm varjatud süsteemi sageli üles võrgust või instrumentarvutist ja üleslaadimise aeg oli mõnikord probleem. Nullidest tulvil puhvrite väljajätmine võib aega oluliselt lühendada.)

Kui vajan manustatud süsteemis dünaamilist mälu eraldamist, siis tavaliselt malloc () või soovitavalt staatiliselt eraldage suur kogum ja jagage see fikseeritud suurusega puhvriteks (või vastavalt üheks väikeste ja suurte puhvrite kogumiks) ja tehke oma eraldamine / eraldamine sellest kogumist. Seejärel rahuldatakse ükskõik millise mälu koguse taotlus kuni puhvri fikseeritud suurusega ühe neist puhvritest. Helistamisfunktsioon ei pea teadma, kas see on suurem kui taotletakse, ja vältides plokkide jagamist ja uuesti ühendamist, lahendame killustatuse. Muidugi võib mälulekkeid siiski tekkida, kui programmil on vead eraldatud / eraldatud.

Teine ajalooline märkus: see viis kiiresti BSS-i segmendi juurde, mis võimaldas programmis nullida oma mälu initsialiseerimiseks, ilma et programmide laadimise ajal nullid aeglaselt kopeeritakse.
#2
+18
Edgar Bonet
2015-03-01 19:51:45 UTC
view on stackexchange narkive permalink

Vaatasin malloc () -i algoritmi avr-libc-st ja näib, et on vähe kasutamisharjumusi, mis on kuhjamise seisukohalt ohutud:

1. Eraldage ainult pikaealised puhvrid

Selle all mõtlen: eraldage programmi alguses kõik vajalik ja ärge kunagi vabastage seda. Muidugi võiksite sellisel juhul kasutada ka staatilisi puhvreid ...

2. Eraldage ainult lühiajalised puhvrid

Tähendus: enne muu eraldamist vabastate puhvri. Areasonable'i näide võib välja näha järgmine:

  void foo () {size_t size = figure_out_needs (); char * puhver = malloc (suurus); kui (! puhver) ebaõnnestub (); tee_mis iganes_puhvriga; tasuta (puhver);}  

Kui do_whatever_with () -i sees pole malloci või kui see funktsioon vabastab kõik, mida see eraldab, siis olete killustatuse eest kaitstud.

3. Alati vabastage viimane eraldatud puhver

See on kahe eelmise juhtumi üldistus. Kui kasutate heaplike'i virna (viimane on esimene välja), käitub see nagu virn ja mitte fragment. Tuleb märkida, et sel juhul on viimane eraldatud puhver ohutu toresiseerida koodiga realloc().

4. Määrake alati sama suurus

See ei takista killustumist, kuid on selles mõttes ohutu, et hunnik ei kasva suuremaks kui kasutatud maksimaalne suurus. Kui kõigil teie ostjatel on sama suurus, võite olla kindel, et alati kui üks neist vabastate, on pesa järgmisteks jaotusteks saadaval.

Mustrit 2 tuleks vältida, kuna see lisab malloc () ja free () tsükleid, kui seda saab teha "char buffer [size]" abil; (C ++). Lisan ka anti-mustri "Mitte kunagi ISR-ist".
#3
+17
jfpoilpret
2014-03-09 22:07:19 UTC
view on stackexchange narkive permalink

Tavaliselt väldite Arduino visandite kirjutamisel dünaamilist jaotust (olgu see siis Ccode + eksemplaride puhul malloc või new ), inimesed kasutavad pigem globaalset -või staatiline - muutujad või kohalikud (virna) muutujad.

Dünaamilise jaotuse kasutamine võib põhjustada mitmeid probleeme:

  • mälu lekib (kui kaotate osuti varem eraldatud mälu või tõenäolisem, kui unustate eraldatud mälu vabastada, kui te seda enam ei vaja)
  • kuhjaga killustatus (pärast mitu malloc / tasuta kõned), kus kuhja kasvab suuremaks kui praegu eraldatud mälu tegelik maht

Enamikus olukordades, millega olen kokku puutunud, ei olnud dünaamiline eraldamine kas vajalik või seda sai vältida makrod nagu järgmises koodinäites:

MySketch.ino

  #define BUFFER_SIZE 32 # include "Dummy.h"  

  • Dummy.h
      class Dummy {baidipuhver [BUFFER_SIZE]; ...};  

    Ilma #define BUFFER_SIZE -ta, kui me tahtsime, et klassil Dummy oleks fikseerimata puhver suurus, peaksime kasutama dünaamilist jaotust järgmiselt:

      class Dummy {const byte * buffer; public: Dummy (int size): puhver (uus bait [size]) {} ~ Dummy () {delete [] bufer; }};  

    Sel juhul on meil rohkem võimalusi kui esimeses proovis (nt kasutage erinevaid Dummy objekte, millel on erinevad puhvrid suurus), kuid meil võib olla hunniku killustumisprobleeme.

    Pange tähele, et hävitaja kasutamine puhvri dünaamiliselt eraldatud mälu vabastamiseks vabaneb, kui Dummy eksemplar kustutatakse.

  • #4
    +9
    Peter Bloomfield
    2014-03-09 23:32:04 UTC
    view on stackexchange narkive permalink

    Dünaamilise jaotuse kasutamine ( malloc / free või new / delete kaudu) pole oma olemuselt halb, kuna sellised. Tegelikult on stringide töötlemiseks (nt objekti String kaudu) sageli üsna kasulik. Seda seetõttu, et paljudes visandites kasutatakse mitut väikest stringi fragmenti, mis lõpuks ühendatakse suuremaks. Dünaamilise jaotuse kasutamine võimaldab teil kasutada ainult nii palju mälu kui vaja. Seevastu fikseeritud suurusega staatilise puhvri kasutamine igaühe jaoks võib lõppeda palju ruumi raiskamisega (põhjustades selle mälu tühjenemise palju kiiremini), kuigi see sõltub täielikult kontekstist.

    Kõigiga sellest hoolimata on väga oluline tagada, et mälukasutus oleks prognoositav. Kui lubate visandil kasutada suvalisi mälumahtusid, sõltuvalt käitamise tingimustest (nt sisend), võib see varem või hiljem probleeme tekitada. Mõnel juhul võib see olla täiesti ohutu, nt. kui te teate , ei anna kasutus kunagi palju juurde. Sketse võib programmeerimise käigus muuta. Varakult tehtud eelduse võib unustada, kui midagi hiljem muudetakse, mille tulemuseks on ettenägematu probleem.

    Tugevuse tagamiseks on tavaliselt parem töötada fikseeritud suurusega puhvritega, kui võimalik, ja kujundada visand toimima nende piiridega juba algusest peale. See tähendab, et visandi edaspidised muudatused või ootamatud käitamisolud ei tohiks loodetavasti põhjustada mäluprobleeme.

    #5
    +8
    StuffAndyMakes
    2015-05-18 19:49:12 UTC
    view on stackexchange narkive permalink

    Ma ei nõustu inimestega, kes arvavad, et te ei tohiks seda kasutada, või on see üldiselt tarbetu. Ma usun, et see võib olla ohtlik, kui te ei tea selle puudusi, kuid see on kasulik. Mul on juhtumeid, kus ma ei tea (ega peakski teadma) struktuuri või puhvri suurust (kompileerimise ajal või käitamise ajal), eriti kui tegemist on raamatukogudega, mille ma maailmale saadan. Olen nõus, et kui teie rakendus tegeleb ainult ühe tuntud struktuuriga, peaksite kompileerimise ajal lihtsalt selles suuruses küpsetama.

    Näide: mul on jadapakettide klass (teek), mis suudab võta suvalise pikkusega andmete kasulikud koormused (võib olla struktuur, massiiv uint16_t jne). Selle klassi saatmisotsas ütlete lihtsalt meetodile Packet.send () selle aadressi, mille soovite saata, ja riistvara seeria porti, mille kaudu soovite selle saata. Kuid vastuvõtvas otsas vajan selle sissetuleva kasuliku koormuse hoidmiseks dünaamiliselt eraldatud vastuvõtupuhvrit, kuna see kasulik koormus võib olla igal ajahetkel erinev struktuur, sõltuvalt näiteks rakenduse olekust. KUI ma saadan kunagi ainult ühte struktuuri edasi-tagasi, siis muudaksin puhvri just selliseks, nagu see kompileerimise ajal peab olema. Kuid juhul, kui paketid võivad olla aja jooksul erineva pikkusega, pole malloc () ja free () nii halvad.

    Olen mitu päeva järgmise koodiga testinud, lastes sellel pidevalt tsüklit teha, ja ma pole leidnud tõendeid mälu killustatuse kohta. Pärast dünaamiliselt eraldatud mälu vabastamist naaseb vaba summa oma eelmise väärtuse juurde.

      // leitud saidilt learn.adafruit.com/memories-of-an-arduino/measuring-free-memoryint freeRam () {extern int __heap_start, * __ brkval; int v; return (int) &v - (__brkval == 0? (int) &__heap_start: (int) __brkval);} uint8_t * _tester; while (1) {uint8_t len ​​= juhuslik (1, 1000); Serial.println ("-------------------------------------"); Serial.println ("len on" + string (len, DEC)); Serial.println ("RAM:" + String (freeRam (), DEC));
    Serial.println ("_ tester =" + string ((uint16_t) _tester, DEC)); Serial.println ("testmälu jaotamine"); _tester = (uint8_t *) malloc (len); Serial.println ("RAM:" + String (freeRam (), DEC)); Serial.println ("_ tester =" + string ((uint16_t) _tester, DEC)); Serial.println ("Täites _tester"); for (uint8_t i = 0; i < len; i ++) {_tester [i] = 255; } Serial.println ("RAM:" + string (freeRam (), DEC)); Serial.println ("testimälu vabastamine"); tasuta (_tester); _tester = NULL; Serial.println ("RAM:" + string (freeRam (), DEC)); Serial.println ("_ tester =" + string ((uint16_t) _tester, DEC)); viivitus (1000); // kiire pilk}  

    Ma ei ole näinud mingisugust RAM-i halvenemist ega võimet seda meetodit kasutades dünaamiliselt eraldada, nii et ma ütleksin, et see on toimiv tööriist. FWIW.

    Teie testkood vastab kasutusmustrile _2. Eraldage ainult lühiajalised puhvrid_, mida kirjeldasin oma eelmises vastuses. See on üks väheseid teadaolevalt ohutuid kasutusviise.
    Teisisõnu, probleemid kerkivad esile siis, kui hakkate protsessorit teise * tundmatu * koodiga jagama - see on just see probleem, mida teie arvates väldite. Üldiselt, kui soovite midagi, mis linkimise ajal alati töötab või ebaõnnestub, määrate maksimaalse suuruse fikseeritud jaotuse ja kasutate seda ikka ja jälle, näiteks paludes kasutajal see teile initsialiseerimisel edastada. Pidage meeles, et tavaliselt töötate kiibil, kus ** kõik ** peab mahtuma 2048 baiti - võib-olla mõnel plaadil rohkem, aga teistel võib-olla palju vähem.
    @EdgarBonet Jah, täpselt. Tahtsin lihtsalt jagada.
    @ChrisStratton Minu arvates on ideaalne ainult hetkel vajaliku suurusega puhvri dünaamiline eraldamine. Kui puhvrit pole tavapärase töö ajal vaja, on mälu saadaval millegi muu jaoks. Kuid ma näen, mida te ütlete: eraldage reserveeritud ruum, et midagi muud ei võtaks puhvri jaoks vajalikku ruumi ära. Kogu suurepärane teave ja mõtted.
    Ainult vajaliku suurusega puhvri dünaamiline eraldamine on riskantne, sest kui midagi muud eraldatakse enne selle vabastamist, võib jääda killustatus - mälu, mida te ei saa uuesti kasutada. Samuti on dünaamilisel eraldamisel jälgimise üldkulud. Fikseeritud eraldamine ei tähenda, et te ei saaks mälu paljundada, vaid see tähendab, et jagamine tuleb oma programmi kujundada. Puhtalt kohaliku ulatusega puhvri puhul võite kaaluda ka virna kasutamist. Te pole kontrollinud ka malloc () ebaõnnestumise võimalust.
    @ChrisStratton Õige, see kooditükk ei kontrolli, kas malloc () töötab või mitte. Veelkord, see kooditükk kasutab tuntud rasvaseid Arduino teeke ja rutiini. :) Täname ülevaate eest. Väga mõistlik ja väärtuslik.
    "see võib olla ohtlik, kui te ei tea selle puudusi, kuid see on kasulik." võtab üsna palju kokku kogu C / C ++ arenduse. :-)
    #6
    +4
    Mikael Patel
    2017-10-19 15:55:16 UTC
    view on stackexchange narkive permalink

    Kas Malloc () ja free () kasutamine Arduinoga on tõesti halb mõte?

    Lühike vastus on jah. Allpool on toodud põhjused, miks:

    Selle eesmärk on mõista, mis on MPU ja kuidas programmeerida olemasolevate ressursside piires. Arduino Uno kasutab ATmega328p MPU-d 32KB ISP välkmälu, 1024B EEPROMi ja 2KB SRAM-iga. See pole palju mäluallikaid.

    Pidage meeles, et 2KB SRAM-i kasutatakse kõigi globaalsete muutujate, stringiliitlite, virna ja hunniku võimaliku kasutamise jaoks. Virnas peab olema ka ISR-i jaoks ruumi.

    mälu paigutus on:

    SRAM map

    Tänapäeval on arvutites / sülearvutites rohkem kui 1 000 000 korda suurem mälumaht. 1 Mbyte vaikevirna niidi kohta pole MPU-s haruldane, kuid täiesti ebareaalne.

    Manustatud tarkvara projekt peab tegema ressursside eelarve. See on ISR-i latentsuse, vajaliku mäluruumi, arvutusvõimsuse, käsitsetsüklite jms hindamine. Kahjuks pole tasuta lõunasööke ja raske reaalajas sisseehitatud programmeerimine on programmeerimisoskuste kõige keerulisem valdamine. / p>

    Aamen sellele: "[H] ARD reaalajas sisseehitatud programmeerimine on programmeerimisoskusest kõige raskem omandada."
    Kas malloci hukkamisaeg on alati sama? Kujutan ette, et malloc võtab rohkem aega, kui ta otsib saadaolevast ramst edasi sobivat pesa? See oleks veel üks argument (kui ramm otsa saab), et mälu ei jagataks liikvel olles?
    @Paul Kuhu algoritmid (malloc ja tasuta) ei ole tavaliselt konstantse täitmisajaga ega sisene uuesti. Algoritm sisaldab otsingu- ja andmestruktuure, mis nõuavad lõimede kasutamisel lukustamist (samaaegsus).
    #7
      0
    Kelly S. French
    2019-05-09 21:31:11 UTC
    view on stackexchange narkive permalink

    Olgu, ma tean, et see on vana küsimus, kuid mida rohkem ma vastuseid läbi loen, seda enam tulen pidevalt tähelepanuväärse tundega.

    Peatumisprobleem on tõeline

    Tundub, et siin on seos Turingi peatamisprobleemiga. Dünaamilise jaotuse lubamine suurendab nimetatud peatamise tõenäosust, nii et küsimus muutub riskitaluvuseks. Ehkki malloc () ebaõnnestumise ja nii edasi toimimise võimalus on mugav, on see siiski kehtiv tulemus. Näib, et OP-i küsimus puudutab ainult tehnikat ja jah, kasutatavate raamatukogude üksikasjad või konkreetne MPU on olulised; vestlus pöördub programmi peatamise või mõne muu ebanormaalse lõpu riski vähendamise poole. Peame tunnistama keskkondade olemasolu, mis taluvad riski oluliselt erinevalt. Minu hobiprojekt ilusate värvide kuvamiseks LED-ribal ei tapa kedagi, kui juhtub midagi ebatavalist, kuid südame-kopsu masina sees olev MCU tõenäoliselt seda teeb.

    Tere, härra Turing. Minu nimi on Hubris.

    Minu LED-riba jaoks pole mul vahet, kas see lukustub, vaid lähtestan selle. Kui ma oleksin südame-kopsu masinas, mida juhib MCU, on selle lukustumise või töövõimetuse tagajärjed sõna otseses mõttes elu ja surm, nii et küsimus malloc () ja free () kohta tuleks jagada selle vahel, kuidas kavandatud programm käsitleb võimalust näidata hr Turingi kuulsat probleemi. Võib olla lihtne unustada, et see on matemaatiline tõestus, ja veenda end, et kui oleme vaid piisavalt targad, saame vältida arvutuspiiride ohvriks langemist.

    Sellel küsimusel peaks olema kaks aktsepteeritud vastust, üks neile, kes on sunnitud The Halting Problemi näkku vahtides pilgutama, ja üks kõigile teistele. Kuigi enamik arduino kasutusviise pole tõenäoliselt missioonikriitilised ega elu-surma rakendused, on erinevus endiselt olemas, olenemata sellest, millist MPU-d kodeerite.

    Ma ei usu, et peatumise probleem kehtib selles konkreetses olukorras, arvestades asjaolu, et kuhja kasutamine pole tingimata meelevaldne. Kui seda kasutatakse täpselt määratletud viisil, muutub kuhjakasutus ennustatavalt "ohutuks". Peatusprobleemi eesmärk oli välja selgitada, kas saab kindlaks teha, mis juhtub tingimata meelevaldse ja mitte nii täpselt määratletud algoritmiga. See kehtib tõesti palju rohkem programmeerimise kohta laiemas tähenduses ja leian, et see pole siin konkreetselt eriti asjakohane. Ma isegi ei arva, et oleks täiesti aus olla aus.
    Tunnistan mõningaid retoorilisi liialdusi, kuid mõte on tegelikult selles, kui soovite käitumist garanteerida, tähendab kuhja kasutamine riskitaset, mis on palju suurem kui ainult virna kasutamisest kinnipidamine.
    #8
    -3
    JSON
    2015-03-01 13:07:39 UTC
    view on stackexchange narkive permalink

    Ei, kuid neid tuleb eraldatud mälu vabastamiseks () kasutada väga ettevaatlikult. Ma pole kunagi aru saanud, miks tuleks inimeste mälu otsest haldamist vältida, kuna see viitab ebakompetentsuse tasemele, mis pole tarkvaraarendusega üldiselt kokkusobiv. / p>

    Ütleme, et kasutate drooni juhtimiseks oma arduinot. Mis tahes viga teie koodi mis tahes osas võib põhjustada selle taevast kukkumise ja kedagi või midagi haavata. Teisisõnu, kui kellelgi puudub malloci kasutamise oskus, ei peaks ta tõenäoliselt üldse kodeerima, kuna on nii palju muid piirkondi, kus väikesed vead võivad põhjustada tõsiseid probleeme.

    Kas malloci põhjustatud vigu on raskem üles leida ja parandada? Jah, aga see on pigem kodeerijate pettumus kui risk. Mis puutub riskidesse, võib teie koodi mis tahes osa olla mallociga võrdselt või riskantsem, kui te ei tee samme selle tagamiseks, et see oleks õigesti tehtud.

    On huvitav, et kasutasite näiteks drooni. Selle artikli (http://mil-embedded.com/articles/justifiably-apis-militaryaerospace-embedded-code/) kohaselt on "ohu tõttu dünaamiline mälu jaotamine DO-178B standardi kohaselt -kriitiline manustatud avioonikakood. "
    DARPA-l on pikaajaline ajalugu, mis võimaldab töövõtjatel välja töötada oma platvormile sobivad spetsifikatsioonid - miks ei peaks nad seda tegema, kui arve maksavad maksumaksjad. Seetõttu maksab nende jaoks 10 miljardi dollari väljatöötamine, mida teised saavad teha 10 000 dollariga. Peaaegu kõlab, nagu kasutaksite sõjatööstuskompleksi ausa viitena.
    Dünaamiline jaotamine näib olevat teie programmi kutse näitama peatamisprobleemis kirjeldatud arvutuspiire. On mõningaid keskkondi, mis suudavad toime tulla väikese sellise peatamise riskiga, ja on olemas keskkondi (kosmos, kaitse, meditsiin jne), mis ei talu mingeid kontrollitavaid riske, seega keelavad need toimingud, mida "ei tohiks" teha ebaõnnestub, sest "see peaks töötama" ei ole piisavalt hea, kui lasete raketti või juhite südame- või kopsumasinat.


    See küsimus ja vastus tõlgiti automaatselt inglise keelest.Algne sisu on saadaval stackexchange-is, mida täname cc by-sa 3.0-litsentsi eest, mille all seda levitatakse.
    Loading...