ใช้ malloc () และฟรี () เป็นความคิดที่ไม่ดีจริงๆใน Arduino หรือไม่?


49

การใช้งานmalloc()และfree()ดูเหมือนว่าค่อนข้างหายากในโลก Arduino มันถูกใช้ใน AVR C บริสุทธิ์บ่อยกว่า แต่ก็ยังมีความระมัดระวัง

มันเป็นความคิดที่ดีที่จะใช้malloc()และfree()กับ Arduino?


2
คุณจะหมดหน่วยความจำเร็วมากจริง ๆ และถ้าคุณรู้ว่าคุณจะใช้หน่วยความจำเท่าไหร่คุณก็อาจจัดสรรหน่วยความจำแบบคงที่ได้เช่นกัน
ratchet freak

1
ฉันไม่รู้ว่ามันเลวร้ายหรือไม่แต่ฉันคิดว่ามันไม่ได้ใช้เพราะคุณแทบจะไม่เคยใช้ RAM สำหรับสเก็ตช์ส่วนใหญ่เลยและมันก็แค่เปลืองแฟลชและวงจรนาฬิกาอันมีค่า นอกจากนี้อย่าลืมเกี่ยวกับขอบเขต (แม้ว่าฉันไม่รู้ว่าพื้นที่นั้นยังคงถูกจัดสรรสำหรับตัวแปรทั้งหมด)
ไม่ระบุชื่อเพนกวิน

4
ตามปกติคำตอบที่ถูกคือ "ขึ้นอยู่กับ" คุณยังไม่ได้ให้ข้อมูลเพียงพอที่จะรู้ว่าการจัดสรรแบบไดนามิกเหมาะสมกับคุณหรือไม่
WineSoaked

คำตอบ:


40

กฎทั่วไปของฉันสำหรับระบบฝังตัวเป็นเพียงบัฟเฟอร์ขนาดใหญ่และเพียงครั้งเดียวในช่วงเริ่มต้นของโปรแกรมเช่นในmalloc() setup()ปัญหาเกิดขึ้นเมื่อคุณจัดสรรและจัดสรรหน่วยความจำ ในช่วงระยะเวลานานหน่วยความจำจะกระจัดกระจายและในที่สุดการจัดสรรล้มเหลวเนื่องจากไม่มีพื้นที่ว่างขนาดใหญ่เพียงพอแม้ว่าหน่วยความจำว่างทั้งหมดจะเพียงพอสำหรับคำขอ

(เปอร์สเปคทีฟประวัติให้ข้ามหากไม่สนใจ): ขึ้นอยู่กับการนำไปใช้ของโหลดเดอร์ข้อได้เปรียบเพียงอย่างเดียวของการจัดสรรแบบรันไทม์กับการจัดสรรเวลาคอมไพล์ (globals แบบเน้นข้อความ) คือขนาดของไฟล์ hex เมื่อระบบฝังตัวถูกสร้างโดยไม่ใช้คอมพิวเตอร์ชั้นวางที่มีหน่วยความจำระเหยโปรแกรมมักจะถูกอัพโหลดไปยังระบบฝังตัวจากเครือข่ายหรือคอมพิวเตอร์เครื่องมือและเวลาในการอัปโหลดบางครั้งก็เป็นปัญหา การออกจากบัฟเฟอร์ที่เต็มไปด้วยเลขศูนย์จากรูปภาพอาจทำให้เวลาสั้นลงอย่างมาก)

หากฉันต้องการการจัดสรรหน่วยความจำแบบไดนามิกในระบบฝังตัวฉันโดยทั่วไปmalloc()หรือโดยเฉพาะอย่างยิ่งการจัดสรรแบบคงที่สระว่ายน้ำขนาดใหญ่และแบ่งออกเป็นบัฟเฟอร์ขนาดคงที่ (หรือหนึ่งสระว่ายน้ำแต่ละบัฟเฟอร์ขนาดเล็กและขนาดใหญ่ตามลำดับ) ยกเลิกการจัดสรรจากกลุ่มนั้น จากนั้นทุกคำขอสำหรับจำนวนหน่วยความจำสูงสุดจนถึงขนาดบัฟเฟอร์คงที่จะได้รับเกียรติด้วยหนึ่งในบัฟเฟอร์เหล่านั้น ฟังก์ชั่นการโทรไม่จำเป็นต้องรู้ว่ามันใหญ่กว่าที่ร้องขอหรือไม่และโดยการหลีกเลี่ยงการแยกและรวมบล็อกใหม่ แน่นอนว่าการรั่วไหลของหน่วยความจำยังคงเกิดขึ้นได้หากโปรแกรมได้จัดสรร / ยกเลิกการจัดสรรบั๊ก


อีกบันทึกย่อเชิงประวัติสิ่งนี้นำไปสู่เซ็กเมนต์ BSS อย่างรวดเร็วซึ่งอนุญาตให้โปรแกรมเป็นศูนย์หน่วยความจำของตนเองสำหรับการเริ่มต้นโดยไม่ต้องคัดลอกเลขศูนย์อย่างช้า ๆ ระหว่างการโหลดโปรแกรม
rsaxvc

16

โดยทั่วไปเมื่อเขียนสเก็ตช์ Arduino คุณจะหลีกเลี่ยงการจัดสรรแบบไดนามิก (ไม่ว่าจะด้วยmallocหรือnewสำหรับอินสแตนซ์ C ++) ผู้คนค่อนข้างใช้staticตัวแปรglobal หรือตัวแปร local (stack)

การใช้การจัดสรรแบบไดนามิกอาจทำให้เกิดปัญหาหลายประการ:

  • หน่วยความจำรั่ว (หากคุณสูญเสียตัวชี้ไปยังหน่วยความจำที่คุณจัดสรรไว้ก่อนหน้านี้หรือมีแนวโน้มมากขึ้นถ้าคุณลืมที่จะเพิ่มหน่วยความจำที่จัดสรรเมื่อคุณไม่ต้องการมันอีกต่อไป)
  • การแตกแฟรกเมนต์ของฮีป (หลังจากการโทรหลายครั้งmalloc/ ครั้งfree) ซึ่งฮีปมีขนาดใหญ่กว่าจำนวนหน่วยความจำจริงที่จัดสรรในปัจจุบัน

ในสถานการณ์ส่วนใหญ่ที่ฉันเผชิญการจัดสรรแบบไดนามิกไม่จำเป็นหรือสามารถหลีกเลี่ยงได้ด้วยแมโครเช่นเดียวกับในตัวอย่างรหัสต่อไปนี้:

MySketch.ino

#define BUFFER_SIZE 32
#include "Dummy.h"

Dummy.h

class Dummy
{
    byte buffer[BUFFER_SIZE];
    ...
};

หากไม่มี#define BUFFER_SIZEเราต้องการให้Dummyคลาสมีขนาดไม่คงที่bufferเราจะต้องใช้การจัดสรรแบบไดนามิกดังนี้:

class Dummy
{
    const byte* buffer;

    public:
    Dummy(int size):buffer(new byte[size])
    {
    }

    ~Dummy()
    {
        delete [] bufer;
    }
};

ในกรณีนี้เรามีตัวเลือกมากกว่าในตัวอย่างแรก (เช่นใช้Dummyวัตถุต่างๆ ที่มีbufferขนาดแตกต่างกันสำหรับแต่ละคน) แต่เราอาจมีปัญหาการกระจายตัวของฮีป

หมายเหตุการใช้ destructor เพื่อให้แน่ใจว่าหน่วยความจำที่จัดสรรแบบไดนามิกสำหรับbufferจะถูกปลดปล่อยเมื่อDummyอินสแตนซ์ถูกลบ


14

ฉันได้ดูอัลกอริทึมที่ใช้โดยmalloc()จาก avr-libc และดูเหมือนว่าจะมีรูปแบบการใช้งานบางอย่างที่ปลอดภัยจากมุมมองของการแตกแฟรกเมนต์ของฮีป:

1. จัดสรรบัฟเฟอร์ที่มีอายุการใช้งานยาวนานเท่านั้น

โดยสิ่งนี้ฉันหมายถึง: จัดสรรสิ่งที่คุณต้องการในตอนเริ่มต้นของโปรแกรมและไม่ปล่อยให้เป็นอิสระ แน่นอนในกรณีนี้คุณสามารถใช้บัฟเฟอร์คงที่ ...

2. จัดสรรบัฟเฟอร์สั้น ๆ เท่านั้น

ความหมาย: คุณทำให้บัฟเฟอร์ว่างก่อนที่จะจัดสรรสิ่งอื่นใด ตัวอย่างที่สมเหตุสมผลอาจมีลักษณะเช่นนี้:

void foo()
{
    size_t size = figure_out_needs();
    char * buffer = malloc(size);
    if (!buffer) fail();
    do_whatever_with(buffer);
    free(buffer);
}

หากไม่มี malloc อยู่ภายในdo_whatever_with()หรือถ้าฟังก์ชันนั้นปลดปล่อยสิ่งที่จัดสรรไว้คุณก็ปลอดภัยจากการแตกแฟรกเมนต์

3. ปลดปล่อยบัฟเฟอร์ที่จัดสรรล่าสุดเสมอ

นี่เป็นลักษณะทั่วไปของสองกรณีก่อนหน้านี้ หากคุณใช้ฮีปเหมือนสแต็ก (เข้ามาก่อนจะออกมาก่อน) จากนั้นมันจะทำงานเหมือนสแต็กและไม่ใช่แฟรกเมนต์ realloc()มันควรจะตั้งข้อสังเกตว่าในกรณีนี้มันมีความปลอดภัยในการปรับขนาดบัฟเฟอร์ที่ผ่านมากับการจัดสรร

4. จัดสรรขนาดเดียวกันทุกครั้ง

นี้จะไม่ป้องกันการกระจายตัว แต่มันมีความปลอดภัยในแง่ที่ว่ากองจะไม่เติบโตมีขนาดใหญ่กว่าสูงสุดที่ใช้ขนาด หากบัฟเฟอร์ทั้งหมดของคุณมีขนาดเท่ากันคุณสามารถมั่นใจได้ว่าเมื่อใดก็ตามที่คุณว่างหนึ่งในนั้นจะมีสล็อตสำหรับการจัดสรรในภายหลัง


1
ควรหลีกเลี่ยงรูปแบบ 2 เนื่องจากเพิ่มวงจรสำหรับ malloc () และ free () เมื่อสามารถทำได้ด้วย "char buffer [size];" (ใน C ++) ฉันต้องการเพิ่มรูปแบบการต่อต้าน "Never from the ISR"
Mikael Patel

9

การใช้การจัดสรรแบบไดนามิก (ผ่านmalloc/ freeหรือnew/ delete) ไม่ได้เลวร้ายอย่างที่เป็นเช่นนี้ อันที่จริงแล้วสำหรับบางอย่างเช่นการประมวลผลสตริง (เช่นผ่านStringวัตถุ) มักจะมีประโยชน์มาก นั่นเป็นเพราะสเก็ตช์จำนวนมากใช้ชิ้นส่วนเล็ก ๆ ของสตริงซึ่งในที่สุดจะรวมกันเป็นส่วนที่ใหญ่กว่า การใช้การจัดสรรแบบไดนามิกช่วยให้คุณใช้หน่วยความจำได้มากเท่าที่คุณต้องการสำหรับแต่ละหน่วย ในทางตรงกันข้ามการใช้บัฟเฟอร์คงที่ขนาดคงที่สำหรับแต่ละอันอาจทำให้สิ้นเปลืองพื้นที่มาก (ทำให้หน่วยความจำหมดเร็วขึ้นมาก) แม้ว่ามันจะขึ้นอยู่กับบริบททั้งหมด

จากข้อมูลทั้งหมดที่กล่าวมาเป็นสิ่งสำคัญมากเพื่อให้แน่ใจว่าการใช้งานหน่วยความจำสามารถคาดเดาได้ การอนุญาตให้ร่างภาพใช้หน่วยความจำตามอำเภอใจโดยขึ้นอยู่กับสถานการณ์ขณะทำงาน (เช่นอินพุต) สามารถทำให้เกิดปัญหาได้อย่างง่ายดายไม่ช้าก็เร็ว ในบางกรณีมันอาจปลอดภัยอย่างสมบูรณ์เช่นถ้าคุณรู้ว่าการใช้งานจะไม่เพิ่มขึ้นมาก ภาพร่างสามารถเปลี่ยนแปลงได้ในระหว่างขั้นตอนการเขียนโปรแกรม การสันนิษฐานในช่วงต้นอาจถูกลืมได้เมื่อมีบางสิ่งเปลี่ยนไปในภายหลังทำให้เกิดปัญหาที่ไม่คาดฝัน

เพื่อความแข็งแกร่งมักจะดีกว่าที่จะทำงานกับบัฟเฟอร์ขนาดคงที่ถ้าเป็นไปได้และออกแบบร่างให้ทำงานอย่างชัดเจนกับข้อ จำกัด เหล่านั้นตั้งแต่เริ่มแรก นั่นหมายถึงการเปลี่ยนแปลงร่างใด ๆ ในอนาคตหรือสถานการณ์รันไทม์ที่คาดไม่ถึงหวังว่าจะไม่ทำให้เกิดปัญหาหน่วยความจำ


6

ฉันไม่เห็นด้วยกับคนที่คิดว่าคุณไม่ควรใช้หรือโดยทั่วไปไม่จำเป็น ฉันเชื่อว่ามันอาจเป็นอันตรายได้หากคุณไม่ทราบรายละเอียด แต่ก็มีประโยชน์ ฉันมีหลายกรณีที่ฉันไม่รู้ (และไม่ควรสนใจ) ขนาดของโครงสร้างหรือบัฟเฟอร์ (ในเวลารวบรวมหรือเวลาทำงาน) โดยเฉพาะอย่างยิ่งเมื่อมันมาถึงห้องสมุดที่ฉันส่งออกไปสู่โลก ฉันยอมรับว่าหากแอปพลิเคชันของคุณเกี่ยวข้องกับโครงสร้างเดียวที่รู้จักคุณควรอบในขนาดนั้นในเวลารวบรวม

ตัวอย่าง: ฉันมีคลาสแพ็กเก็ตแบบอนุกรม (ไลบรารี) ที่สามารถรับข้อมูล payload ได้ตามอำเภอใจ (สามารถเป็นโครงสร้างอาร์เรย์ของ uint16_t เป็นต้น) ในตอนท้ายของการส่งของคลาสนั้นคุณเพียงแค่บอก Packet.send () วิธีการที่อยู่ของสิ่งที่คุณต้องการที่จะส่งและพอร์ต HardwareSerial ที่คุณต้องการที่จะส่ง อย่างไรก็ตามในตอนท้ายของการรับฉันต้องการบัฟเฟอร์การรับที่จัดสรรแบบไดนามิกเพื่อเก็บ payload ที่เข้ามาเนื่องจาก payload นั้นอาจเป็นโครงสร้างที่แตกต่างกันในช่วงเวลาใดก็ตามขึ้นอยู่กับสถานะของแอปพลิเคชัน ถ้าฉันเพิ่งส่งโครงสร้างเดียวกลับไปกลับมาฉันจะทำให้บัฟเฟอร์มีขนาดที่จะต้องรวบรวม แต่ในกรณีที่แพ็กเก็ตอาจมีความยาวต่างกันเมื่อเวลาผ่านไป malloc () และ free () จะไม่เลวร้ายนัก

ฉันรันการทดสอบโดยใช้รหัสต่อไปนี้เป็นเวลาหลายวันปล่อยให้มันวนซ้ำอย่างต่อเนื่องและฉันไม่พบหลักฐานของการแตกแฟรกเมนต์ของหน่วยความจำ หลังจากการปลดปล่อยหน่วยความจำที่จัดสรรแบบไดนามิกจำนวนที่ว่างจะกลับไปเป็นค่าก่อนหน้า

// found at learn.adafruit.com/memories-of-an-arduino/measuring-free-memory
int 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 = random(1, 1000);
    Serial.println("-------------------------------------");
    Serial.println("len is " + String(len, DEC));
    Serial.println("RAM: " + String(freeRam(), DEC));
    Serial.println("_tester = " + String((uint16_t)_tester, DEC));
    Serial.println("alloating _tester memory");
    _tester = (uint8_t *)malloc(len);
    Serial.println("RAM: " + String(freeRam(), DEC));
    Serial.println("_tester = " + String((uint16_t)_tester, DEC));
    Serial.println("Filling _tester");
    for (uint8_t i = 0; i < len; i++) {
        _tester[i] = 255;
    }
    Serial.println("RAM: " + String(freeRam(), DEC));
    Serial.println("freeing _tester memory");
    free(_tester); _tester = NULL;
    Serial.println("RAM: " + String(freeRam(), DEC));
    Serial.println("_tester = " + String((uint16_t)_tester, DEC));
    delay(1000); // quick look
}

ฉันไม่ได้เห็นการย่อยสลายใด ๆ ใน RAM หรือความสามารถในการจัดสรรมันแบบไดนามิกโดยใช้วิธีนี้ดังนั้นฉันจึงบอกว่ามันเป็นเครื่องมือที่ทำงานได้ FWIW


2
รหัสทดสอบของคุณเป็นไปตามรูปแบบการใช้งาน2 จัดสรรเฉพาะบัฟเฟอร์อายุสั้นที่ฉันอธิบายไว้ในคำตอบก่อนหน้าของฉัน นี่เป็นหนึ่งในรูปแบบการใช้งานที่มีความปลอดภัย
Edgar Bonet

ปัญหาจะเกิดขึ้นเมื่อคุณเริ่มแชร์โปรเซสเซอร์กับรหัสที่ไม่รู้จักอื่น ๆซึ่งเป็นปัญหาที่คุณคิดว่าคุณหลีกเลี่ยง โดยทั่วไปหากคุณต้องการบางสิ่งบางอย่างที่จะใช้งานไม่ได้หรือล้มเหลวในระหว่างการเชื่อมโยงคุณทำการจัดสรรขนาดสูงสุดและใช้ซ้ำแล้วซ้ำอีกตัวอย่างเช่นโดยให้ผู้ใช้ของคุณส่งต่อให้คุณในการเริ่มต้น จำไว้ว่าคุณมักจะทำงานบนชิปที่ทุกอย่างต้องมีขนาดพอดีกับ 2048 ไบต์ - อาจจะมากกว่าในบอร์ดบางตัว
Chris Stratton

@EdgarBonet ใช่แน่นอน แค่อยากจะแบ่งปัน
StuffAndyMakes

1
การจัดสรรบัฟเฟอร์แบบไดนามิกในขนาดที่ต้องการนั้นมีความเสี่ยงราวกับว่ามีสิ่งใดจัดสรรอยู่ก่อนที่คุณจะปล่อยให้เป็นอิสระคุณสามารถปล่อยให้มีการแตกแฟรกเมนต์ - หน่วยความจำที่คุณไม่สามารถนำกลับมาใช้ใหม่ได้ นอกจากนี้การจัดสรรแบบไดนามิกมีการติดตามค่าใช้จ่าย การจัดสรรแบบคงที่ไม่ได้หมายความว่าคุณไม่สามารถใช้หน่วยความจำทวีคูณได้ แต่ก็หมายความว่าคุณต้องทำงานร่วมกันในการออกแบบโปรแกรมของคุณ สำหรับบัฟเฟอร์ที่มีขอบเขตโลคัลเพียงอย่างเดียวคุณอาจมีน้ำหนักในการใช้สแต็ก คุณยังไม่ได้ตรวจสอบความเป็นไปได้ของ malloc () ที่ล้มเหลวเช่นกัน
Chris Stratton

1
"อาจเป็นอันตรายได้หากคุณไม่ทราบรายละเอียด แต่ก็มีประโยชน์" ผลรวมการพัฒนาทั้งหมดใน C / C ++ :-)
ThatAintWorking

4

เป็นความคิดที่ดีจริงๆหรือไม่ที่จะใช้ malloc () และฟรี () กับ Arduino

คำตอบสั้น ๆ คือใช่ ด้านล่างนี้คือเหตุผลว่าทำไม:

มันคือทั้งหมดที่เกี่ยวกับการทำความเข้าใจว่า MPU คืออะไรและวิธีการโปรแกรมภายในข้อ จำกัด ของทรัพยากรที่มีอยู่ Arduino Uno ใช้ATmega328p MPU พร้อมหน่วยความจำแฟลช 32KB ISP, 1024B EEPROM และ SRK 2KB นั่นไม่ใช่ทรัพยากรหน่วยความจำจำนวนมาก

โปรดจำไว้ว่า 2KB SRAM ใช้สำหรับตัวแปรโกลบอลสตริงตัวอักษรสแต็กและการใช้ฮีปที่เป็นไปได้ทั้งหมด สแต็กยังต้องมีห้องหัวสำหรับ ISR

รูปแบบหน่วยความจำคือ:

แผนที่ SRAM

พีซี / แล็ปท็อปปัจจุบันมีหน่วยความจำมากกว่า 1,000,000 ครั้ง พื้นที่สแต็กเริ่มต้น 1 Mbyte ต่อเธรดไม่ใช่เรื่องแปลก แต่ไม่สมจริงทั้งหมดใน MPU

โครงการซอฟต์แวร์แบบฝังตัวต้องทำงบประมาณทรัพยากร นี่เป็นการประมาณเวลาแฝงของ ISR, พื้นที่หน่วยความจำที่จำเป็น, กำลังประมวลผล, วงจรการเรียนการสอน ฯลฯ โชคไม่ดีที่ไม่มีอาหารกลางวันและการเขียนโปรแกรมแบบเรียลไทม์แบบฝังตัวยากที่สุดของทักษะการเขียนโปรแกรม


แก้ไขให้ดีขึ้น: "[H] การเขียนโปรแกรมแบบเรียลไทม์แบบฝังตัวเป็นทักษะการเขียนโปรแกรมที่ยากที่สุดในการฝึกฝน"
StuffAndyM ทำให้

เวลาดำเนินการของ malloc เหมือนกันหรือไม่ ฉันสามารถจินตนาการ malloc ใช้เวลามากขึ้นในขณะที่ค้นหาเพิ่มเติมใน ram ที่มีอยู่สำหรับสล็อตที่พอดีหรือไม่ นี่จะเป็นอีกเหตุผลหนึ่ง (นอกเหนือจาก ram หมด) เพื่อไม่จัดสรรหน่วยความจำขณะเดินทาง
พอล

@Paul อัลกอริทึมฮีป (malloc และฟรี) โดยทั่วไปจะไม่ใช่เวลาดำเนินการคงที่และไม่ใช่การส่งซ้ำ อัลกอริทึมมีโครงสร้างการค้นหาและข้อมูลที่ต้องการการล็อคเมื่อใช้เธรด (การทำงานพร้อมกัน)
Mikael Patel

0

ตกลงฉันรู้ว่านี่เป็นคำถามเก่า แต่ยิ่งฉันอ่านคำตอบมากเท่าไหร่ฉันก็ยิ่งกลับมาดูข้อสังเกตที่สำคัญ

ปัญหาการหยุดชะงักเป็นเรื่องจริง

ดูเหมือนจะมีลิงค์กับปัญหาการหยุดชะงักของทัวริงที่นี่ การอนุญาตให้การจัดสรรแบบไดนามิกเพิ่มโอกาสในการพูดว่า 'หยุด' ดังนั้นคำถามจะกลายเป็นหนึ่งในการยอมรับความเสี่ยง แม้ว่าจะสะดวกในการโบกมือให้เป็นไปได้ของmalloc()ความล้มเหลวและอื่น ๆ แต่ก็ยังเป็นผลลัพธ์ที่ถูกต้อง คำถามที่ OP ถามดูเหมือนจะเป็นเรื่องเกี่ยวกับเทคนิคเท่านั้นและใช่รายละเอียดของห้องสมุดที่ใช้หรือ MPU เฉพาะนั้นมีความสำคัญ บทสนทนาจะลดความเสี่ยงของการหยุดโปรแกรมหรือสิ้นสุดผิดปกติอื่น ๆ เราจำเป็นต้องตระหนักถึงการมีอยู่ของสภาพแวดล้อมที่ทนต่อความเสี่ยงต่างกันอย่างมากมาย งานอดิเรกของฉันในการแสดงสีสวย ๆ บน LED-strip จะไม่ฆ่าใครถ้ามีอะไรผิดปกติเกิดขึ้น แต่ MCU ภายในเครื่องหัวใจปอดน่าจะเป็น

สวัสดีนายทัวริงชื่อของฉันคือ Hubris

สำหรับ LED-strip ของฉันฉันไม่สนใจว่ามันจะล็อคฉันจะรีเซ็ตมัน ถ้าฉันอยู่บนเครื่องหัวใจ - หัวใจที่ถูกควบคุมโดย MCU ผลที่ตามมาของการล็อคหรือล้มเหลวในการทำงานคือชีวิตและความตายดังนั้นคำถามเกี่ยวกับmalloc()และfree()ควรแยกระหว่างวิธีที่โปรแกรมตั้งใจจัดการกับความเป็นไปได้ที่จะแสดงให้เห็นว่านาย ปัญหาที่โด่งดังของทัวริง มันง่ายที่จะลืมว่ามันเป็นข้อพิสูจน์ทางคณิตศาสตร์และเพื่อโน้มน้าวตัวเราเองว่าหากเราฉลาดเท่านั้นเราสามารถหลีกเลี่ยงการสูญเสียขีด จำกัด ของการคำนวณ

คำถามนี้ควรมีคำตอบที่ยอมรับสองคำตอบหนึ่งข้อสำหรับผู้ที่ถูกบังคับให้กระพริบตาเมื่อจ้องมองปัญหา Halting ในหน้าและอีกคำตอบหนึ่งสำหรับผู้อื่นทั้งหมด ในขณะที่การใช้งานส่วนใหญ่ของ Arduino นั้นไม่น่าจะเป็นภารกิจสำคัญหรือแอปพลิเคชันที่ใช้ชีวิตและความตายความแตกต่างยังคงอยู่ที่นั่นโดยไม่คำนึงถึง MPU ที่คุณอาจเข้ารหัส


ฉันไม่คิดว่าปัญหาการหยุดใช้งานในสถานการณ์เฉพาะนี้โดยพิจารณาว่าการใช้ฮีปไม่จำเป็นต้องมีเหตุผล หากใช้ในลักษณะที่กำหนดไว้อย่างดีการใช้ฮีปจะกลายเป็น "ปลอดภัย" อย่างแน่นอน ประเด็นของปัญหาการหยุดชะงักคือการพิจารณาว่าสามารถระบุได้ว่าเกิดอะไรขึ้นกับอัลกอริทึมที่กำหนดเองและไม่ได้กำหนดไว้อย่างดี มันใช้กับการเขียนโปรแกรมในวงกว้างมากขึ้นและฉันคิดว่ามันไม่เกี่ยวข้องกับที่นี่ ฉันไม่คิดว่ามันจะเกี่ยวข้องเลยที่จะซื่อสัตย์
Jonathan Grey

ฉันจะยอมรับวาทศิลป์ที่พูดเกินจริงบางอย่าง แต่ประเด็นก็คือถ้าคุณต้องการรับประกันพฤติกรรมการใช้ heap จะกำหนดระดับความเสี่ยงที่สูงกว่าการเกาะกลุ่มโดยใช้เพียงกองซ้อนเท่านั้น
Kelly S. French

-3

ไม่ได้ แต่จะต้องใช้อย่างระมัดระวังเกี่ยวกับการฟรี () ในการจัดสรรหน่วยความจำ ฉันไม่เคยเข้าใจว่าทำไมคนพูดว่าการจัดการหน่วยความจำโดยตรงควรหลีกเลี่ยงเพราะมันหมายถึงระดับของความสามารถที่ไม่สอดคล้องกับการพัฒนาซอฟต์แวร์โดยทั่วไป

ให้บอกว่าคุณใช้ Arduino ของคุณเพื่อควบคุมเสียงพึมพำ ข้อผิดพลาดใด ๆ ในส่วนใด ๆ ของรหัสของคุณอาจทำให้รหัสนี้หลุดออกมาจากท้องฟ้าและทำร้ายใครบางคนหรือบางสิ่งบางอย่าง กล่าวอีกนัยหนึ่งถ้ามีคนขาดความสามารถในการใช้ malloc พวกเขาอาจไม่ควรเข้ารหัสเพราะมีพื้นที่อื่น ๆ มากมายที่มีข้อบกพร่องเล็ก ๆ อาจทำให้เกิดปัญหาร้ายแรง

ข้อบกพร่องที่เกิดจาก malloc ยากที่จะติดตามและแก้ไขหรือไม่ ใช่ แต่นั่นเป็นเรื่องของความคับข้องใจในตัวแปลงสัญญาณมากกว่าความเสี่ยง เท่าที่ความเสี่ยงเกิดขึ้นส่วนใด ๆ ของรหัสของคุณอาจมีความเสี่ยงเท่ากันหรือมากกว่า malloc หากคุณไม่ทำตามขั้นตอนเพื่อให้แน่ใจว่าถูกต้อง


4
มันน่าสนใจที่คุณใช้เสียงพึมพำเป็นตัวอย่าง อ้างอิงจากบทความนี้ ( mil-embedded.com/articles/ … ), "เนื่องจากความเสี่ยงการจัดสรรหน่วยความจำแบบไดนามิกจึงเป็นสิ่งต้องห้ามภายใต้มาตรฐาน DO-178B ในรหัส avionics ที่ฝังตัวเพื่อความปลอดภัย"
Gabriel Staples

DARPA มีประวัติอันยาวนานในการอนุญาตให้ผู้รับเหมาพัฒนารายละเอียดที่เหมาะสมกับแพลตฟอร์มของตัวเอง - ทำไมพวกเขาไม่ควรทำเมื่อเป็นผู้จ่ายภาษีที่จ่ายบิล นี่คือเหตุผลว่าทำไมจึงมีค่าใช้จ่าย $ 10,000 ล้านสำหรับพวกเขาในการพัฒนาสิ่งที่คนอื่นสามารถทำกับ $ 10,000 เกือบฟังดูเหมือนว่าคุณใช้คอมเพล็กซ์อุตสาหกรรมทางทหารเป็นข้อมูลอ้างอิงอย่างซื่อสัตย์
JSON

การจัดสรรแบบไดนามิกดูเหมือนจะเป็นการเชิญให้โปรแกรมของคุณแสดงขีด จำกัด ของการคำนวณที่อธิบายไว้ในปัญหาการหยุดชะงัก มีสภาพแวดล้อมบางอย่างที่สามารถจัดการกับความเสี่ยงเล็กน้อยของการหยุดชะงักและมีสภาพแวดล้อมที่มีอยู่ (พื้นที่การป้องกันการแพทย์ ฯลฯ ) ที่จะไม่ทนต่อความเสี่ยงที่สามารถควบคุมได้จำนวนใด ๆ ดังนั้นพวกเขาจึงไม่อนุญาตการดำเนินการที่ ล้มเหลวเพราะ 'ควรทำงาน' ไม่ดีพอเมื่อคุณเปิดตัวจรวดหรือควบคุมเครื่องจักรหัวใจ / ปอด
Kelly S. French
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.