ব্যবহারের malloc()
এবং free()
যাও Arduino বিশ্বের চমত্কার বিরল বলে মনে হয়। এটি খাঁটি এভিআর সিতে প্রায়শই ব্যবহৃত হয় তবে এখনও সতর্কতার সাথে।
এটি ব্যবহার করা malloc()
এবং free()
আরডুইনো সহ সত্যিই খারাপ ধারণা ?
ব্যবহারের malloc()
এবং free()
যাও Arduino বিশ্বের চমত্কার বিরল বলে মনে হয়। এটি খাঁটি এভিআর সিতে প্রায়শই ব্যবহৃত হয় তবে এখনও সতর্কতার সাথে।
এটি ব্যবহার করা malloc()
এবং free()
আরডুইনো সহ সত্যিই খারাপ ধারণা ?
উত্তর:
এম্বেড থাকা সিস্টেমগুলির জন্য আমার সাধারণ নিয়মটি কেবলমাত্র malloc()
বড় বাফার এবং প্রোগ্রামের শুরুতে, একবারে, একবারে setup()
। আপনি যখন মেমরি বরাদ্দ করেন এবং ডি-বরাদ্দ করেন তখন সমস্যাটি আসে। অনুরোধের জন্য মোট নিখরচর মেমরিটি পর্যাপ্ত পরিমাণের চেয়েও বেশি হলেও দীর্ঘ অংশের অধিবেশনটিতে, মেমরিটি খণ্ডিত হয়ে যায় এবং অবশেষে পর্যাপ্ত পরিমাণ বৃহত মুক্ত অঞ্চল না থাকার কারণে একটি বরাদ্দ ব্যর্থ হয়।
(Icalতিহাসিক দৃষ্টিভঙ্গি, আগ্রহী না হলে এড়িয়ে যান): রানার বাস্তবায়নের উপর নির্ভর করে রান-টাইম বরাদ্দ বনাম কমপ্লাই-টাইম বরাদ্দ (ইনটাইটেলাইজড গ্লোবাল) এর একমাত্র সুবিধা হেক্স ফাইলের আকার। যখন সমস্ত অস্থির মেমরি থাকা শেল্ফ কম্পিউটারগুলি সহ এম্বেড সিস্টেমগুলি নির্মিত হয়েছিল, প্রোগ্রামটি প্রায়শই একটি নেটওয়ার্ক বা কোনও উপকরণ কম্পিউটার থেকে এম্বেড করা সিস্টেমে আপলোড করা হত এবং আপলোড সময়টি কখনও কখনও একটি সমস্যা ছিল। চিত্র থেকে শূন্যে পূর্ণ বাফারগুলি ফেলে রাখা সময়টি যথেষ্ট সংক্ষিপ্ত করতে পারে))
যদি আমার একটি এম্বেড থাকা সিস্টেমে গতিশীল মেমরির বরাদ্দ প্রয়োজন হয় তবে আমি সাধারণত malloc()
বা অগ্রাধিকার হিসাবে স্থিতিশীলভাবে একটি বড় পুল বরাদ্দ করি এবং এটিকে স্থির আকারের বাফারে ভাগ করি (বা একটি পুল যথাক্রমে ছোট এবং বড় বাফারের প্রতিটি) এবং আমার নিজের বরাদ্দ / এই পুল থেকে ডি-বরাদ্দ। তারপরে নির্দিষ্ট বাফার আকার পর্যন্ত যেকোন পরিমাণ মেমরির জন্য প্রতিটি অনুরোধ সেই বাফারগুলির মধ্যে একটির সাথে সম্মানিত হয়। কলিং ফাংশনটি এটি অনুরোধের চেয়ে বড় কিনা তা জানার দরকার নেই এবং বিভক্তকরণ এবং পুনরায় সংযুক্তিগুলি এড়িয়ে আমরা খণ্ডগুলি সমাধান করি। প্রোগ্রামটি বরাদ্দ / ডি-বরাদ্দ থাকলে অবশ্যই মেমরি ফাঁস হতে পারে।
সাধারণত, আরডুইনো স্কেচগুলি লেখার সময়, আপনি গতিশীল বরাদ্দ এড়াতে পারবেন (এটি সি ++ উদাহরণগুলির সাথে malloc
বা এটির new
জন্য), লোকেরা বরং গ্লোবাল-ও static
- ভেরিয়েবল বা স্থানীয় (স্ট্যাক) ভেরিয়েবল ব্যবহার করে।
গতিশীল বরাদ্দ ব্যবহার করে বেশ কয়েকটি সমস্যা দেখা দিতে পারে:
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
আকারের সাথে বিভিন্ন বস্তু ব্যবহার করুন ) তবে আমাদের কাছে হ্যাপ ফ্র্যাগমেন্টেশন সমস্যা থাকতে পারে।
উদাহরণস্বরূপ মুছে ফেলা buffer
হলে জন্য গতিশীলভাবে বরাদ্দ মেমরি মুক্ত করা হবে তা নিশ্চিত করতে একটি ডেস্ট্রাক্টরের ব্যবহার নোট করুন Dummy
।
আমি malloc()
avr-libc থেকে ব্যবহৃত অ্যালগরিদমটি একবার দেখেছি এবং মনে হচ্ছে কয়েকটি ব্যবহারের ধরণ রয়েছে যা গাদা বিভক্তির দৃষ্টিকোণ থেকে নিরাপদ:
এর অর্থ আমার: প্রোগ্রামটির শুরুতে আপনার যা যা প্রয়োজন তা বরাদ্দ করুন এবং এটি কখনই মুক্ত করবেন না। অবশ্যই, এই ক্ষেত্রে, আপনি পাশাপাশি স্থির বাফার ব্যবহার করতে পারেন ...
অর্থ: আপনি অন্য কিছু বরাদ্দের আগে বাফারটি মুক্ত করুন। একটি যুক্তিসঙ্গত উদাহরণ হতে পারে:
void foo()
{
size_t size = figure_out_needs();
char * buffer = malloc(size);
if (!buffer) fail();
do_whatever_with(buffer);
free(buffer);
}
যদি ভিতরে কোনও মলোক না do_whatever_with()
থাকে, বা যদি এই ফাংশনটি যা বরাদ্দ করে তা মুক্ত করে, তবে আপনি খণ্ডন থেকে নিরাপদ।
এটি পূর্ববর্তী দুটি মামলার একটি সাধারণীকরণ। আপনি যদি স্ট্যাকের মতো হিপ ব্যবহার করেন (সর্বশেষে প্রথমটি শেষ হয়ে যায়), তবে এটি স্ট্যাকের মতো আচরণ করবে এবং খণ্ড না। এটি লক্ষ করা উচিত যে এই ক্ষেত্রে এটি সর্বশেষ বরাদ্দযুক্ত বাফারটির সাথে পুনরায় আকার দেওয়া নিরাপদ realloc()
।
এটি খণ্ডিত হওয়া রোধ করবে না, তবে এটি এই অর্থে নিরাপদ যে সর্বাধিক ব্যবহৃত আকারের চেয়ে বড় বড় হবে না । যদি আপনার সমস্ত বাফারগুলির আকার একই থাকে তবে আপনি নিশ্চিত হতে পারেন যে যখনই আপনি এগুলির মধ্যে একটি মুক্ত করেন স্লটটি পরবর্তী বরাদ্দের জন্য উপলব্ধ থাকবে।
গতিশীল বরাদ্দ ব্যবহার ( malloc
/ free
অথবা new
/ মাধ্যমে delete
) এর মতো অন্তর্নিহিত খারাপ নয়। আসলে, স্ট্রিং প্রসেসিংয়ের মতো String
কোনও জিনিসের জন্য (যেমন বস্তুর মাধ্যমে ), এটি প্রায়শই বেশ সহায়ক। এর কারণ অনেক স্কেচ স্ট্রিংয়ের কয়েকটি ছোট ছোট টুকরা ব্যবহার করে যা শেষ পর্যন্ত বড় আকারে একত্রিত হয়। গতিশীল বরাদ্দ ব্যবহার আপনাকে প্রতিটিটির জন্য প্রয়োজন যতটা মেমরি ব্যবহার করতে দেয়। বিপরীতে, প্রত্যেকের জন্য একটি স্থির-আকারের স্ট্যাটিক বাফার ব্যবহার করে প্রচুর জায়গা নষ্ট হয়ে যায় (এটি স্মৃতি থেকে খুব দ্রুত সঞ্চালিত হতে পারে), যদিও এটি পুরোপুরি প্রসঙ্গে নির্ভর করে।
এই সমস্ত কথার সাথে, মেমরির ব্যবহার অনুমানযোগ্য তা নিশ্চিত করা খুব গুরুত্বপূর্ণ। রান-টাইম পরিস্থিতিতে (যেমন ইনপুট) উপর নির্ভর করে স্কেচটিকে নির্বিচার পরিমাণে মেমরির ব্যবহার করার অনুমতি দিলে তাড়াতাড়ি বা খুব সহজেই সমস্যা দেখা দিতে পারে। কিছু ক্ষেত্রে, এটি পুরোপুরি নিরাপদ হতে পারে, উদাহরণস্বরূপ যদি আপনি জানেন তবে ব্যবহারটি কখনও বাড়বে না। প্রোগ্রামিং প্রক্রিয়া চলাকালীন স্কেচগুলি পরিবর্তন হতে পারে। কোনও কিছু পরে পরিবর্তন করা গেলে প্রাথমিকভাবে করা একটি অনুমান ভুলে যেতে পারে, যার ফলে একটি অপ্রত্যাশিত সমস্যা দেখা দেয়।
দৃ rob়তার জন্য, সাধারণত যেখানে সম্ভব স্থির আকারের বাফারগুলির সাথে কাজ করা ভাল এবং শুরু থেকে এই সীমাগুলির সাথে স্পষ্টভাবে কাজ করার জন্য স্কেচটি ডিজাইন করুন। এর অর্থ স্কেচে ভবিষ্যতের যে কোনও পরিবর্তন, বা কোনও অপ্রত্যাশিত রান-টাইম পরিস্থিতি, আশা করি কোনও স্মৃতি সমস্যা তৈরি করে না।
আমি এমন লোকদের সাথে একমত নই যাঁরা মনে করেন আপনার এটি ব্যবহার করা উচিত নয় বা এটি সাধারণত অপ্রয়োজনীয়। আমি বিশ্বাস করি এটি বিপজ্জনক হতে পারে যদি আপনি এর ইনস এবং আউটগুলি না জানেন তবে এটি কার্যকর। আমার এমন কেস রয়েছে যেখানে আমি কাঠামোর আকার বা বাফারের আকার (সংকলন করার সময় বা রান টাইমে) আকারে জানি না (এবং এটি জানা উচিত নয়) বিশেষত যখন আমি লাইব্রেরিতে আসে তখনই আমি বিশ্বে পাঠাই। আমি সম্মত হই যে আপনি যদি অ্যাপ্লিকেশনটি কেবল একটি একক, জ্ঞাত কাঠামোর সাথেই কাজ করে থাকেন তবে আপনাকে সংকলনের সময় সেই আকারটি বেক করা উচিত।
উদাহরণ: আমার কাছে একটি সিরিয়াল প্যাকেট ক্লাস (একটি গ্রন্থাগার) রয়েছে যা নির্বিচারে দৈর্ঘ্যের ডেটা পেলোড নিতে পারে (স্ট্রাক্ট হতে পারে, uint16_t এর অ্যারে ইত্যাদি)। এই শ্রেণীর প্রেরণ শেষে আপনি কেবল প্যাকেট.সেন্ড () পদ্ধতিটি আপনি যে জিনিসটি প্রেরণ করতে চান তার ঠিকানা এবং হার্ডওয়্যারশিয়ার বন্দর যার মাধ্যমে আপনি এটি প্রেরণ করতে চান তা জানান। যাইহোক, প্রাপ্তির শেষে আমার আগত পেডলোডটি ধরে রাখার জন্য একটি গতিশীলভাবে বরাদ্দ প্রাপ্ত রিজার্ভ বাফার দরকার, যেমন অ্যাপ্লিকেশনটির রাজ্যের উপর নির্ভর করে যে কোনও মুহুর্তে পে-লোড আলাদা কাঠামো হতে পারে। যদি আমি কেবল কোনও একক কাঠামো পিছনে পিছনে প্রেরণ করি তবে আমি সংকলনের সময় বাফারটিকে যে আকারের প্রয়োজন তা কেবল তৈরি করব। তবে, যে ক্ষেত্রে প্যাকেটগুলি সময়ের সাথে সাথে বিভিন্ন দৈর্ঘ্যের হতে পারে, ম্যালোক () এবং ফ্রি () এতটা খারাপ নয়।
আমি নিম্নলিখিত কোডগুলি কয়েকদিন ধরে চালিয়েছি, এটিকে অবিরত লুপ করতে দিয়েছি এবং মেমরি খণ্ডিত হওয়ার কোনও প্রমাণ পাইনি। গতিশীল বরাদ্দ মেমরি মুক্ত করার পরে, বিনামূল্যে পরিমাণটি তার আগের মানটিতে ফিরে আসে।
// 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
}
আমি এই পদ্ধতিটি ব্যবহার করে গতিশীলভাবে এটিকে বরাদ্দ দেওয়ার ক্ষমতা বা র্যামে কোনও ধরণের অবক্ষয় দেখিনি, তাই আমি বলব এটি একটি কার্যকর সরঞ্জাম। FWIW।
আরডিনো দিয়ে ম্যালোক () এবং ফ্রি () ব্যবহার করা কি আসলেই খারাপ ধারণা?
সংক্ষিপ্ত উত্তর হল হ্যাঁ. নীচের কারণগুলি:
এটি এমপিইউ কী এবং উপলব্ধ সংস্থানগুলির সীমাবদ্ধতার মধ্যে কীভাবে প্রোগ্রাম করা যায় তা বোঝার বিষয়। আরডুইনো ইউনো 32KB আইএসপি ফ্ল্যাশ মেমরি, 1024 বি ইপ্রোম এবং 2 কেবি এসআরামের সাথে একটি এটিমেগা 328 পি এমপিইউ ব্যবহার করে । এটি প্রচুর স্মৃতি সংস্থান নয়।
মনে রাখবেন যে 2 কেবি এসআরএএম সমস্ত গ্লোবাল ভেরিয়েবল, স্ট্রিং লিটারালস, স্ট্যাক এবং গাদাটির সম্ভাব্য ব্যবহারের জন্য ব্যবহৃত হয়। স্ট্যাকের একটি আইএসআর এর জন্য প্রধান কক্ষ থাকা দরকার।
মেমরি লেআউট হল:
আজকের পিসি / ল্যাপটপগুলিতে মেমরির পরিমাণ 1.000.000 গুণ বেশি। থ্রেড প্রতি 1 এমবিতে ডিফল্ট স্ট্যাক স্পেস অস্বাভাবিক নয় তবে কোনও এমপিইউতে সম্পূর্ণ অবাস্তব।
একটি এম্বেড থাকা সফ্টওয়্যার প্রকল্পের একটি সংস্থান বাজেট করতে হয়। এটি আইএসআর ল্যাটেন্সি, প্রয়োজনীয় মেমরি স্পেস, গণনা শক্তি, নির্দেশচক্র ইত্যাদির অনুমান করছে There দুর্ভাগ্যবশত কোনও ফ্রি-লাঞ্চ নেই এবং হার্ড রিয়েল-টাইম এম্বেডড প্রোগ্রামিং দক্ষতা অর্জনের জন্য প্রোগ্রামিং দক্ষতার পক্ষে সবচেয়ে কঠিন ।
ঠিক আছে, আমি জানি এটি একটি পুরানো প্রশ্ন তবে উত্তরগুলির মধ্য দিয়ে যত বেশি আমি পড়ি ততই আমি পর্যবেক্ষণে ফিরে আসি যা মুখ্য মনে হয়।
এখানে টিউরিংয়ের হ্যালটিং সমস্যার সাথে একটি যোগসূত্র বলে মনে হচ্ছে। গতিশীল বরাদ্দের মঞ্জুরি দেওয়া 'থামানো'র প্রতিক্রিয়া বাড়িয়ে তোলে তাই প্রশ্নটি ঝুঁকি-সহনশীলতার অন্যতম হয়ে ওঠে। যদিও এটির malloc()
ব্যর্থতা এবং এরকম সম্ভাবনা সরিয়ে দেওয়া সুবিধাজনক তবে এটি এখনও একটি বৈধ ফলাফল। ওপি যে প্রশ্নটি করে তা কেবল কৌশল সম্পর্কিত বলেই মনে হয় এবং হ্যাঁ ব্যবহৃত গ্রন্থাগারগুলির বিশদ বা নির্দিষ্ট এমপিইউ গুরুত্বপূর্ণ; কথোপকথনটি কর্মসূচি থামানো বা অন্য কোনও অস্বাভাবিক শেষের ঝুঁকি হ্রাস করার দিকে ঘুরে যায়। আমাদের এমন পরিবেশগুলির অস্তিত্বকে স্বীকৃতি দিতে হবে যা ঝুঁকিটিকে ভিন্নভাবে সহ্য করে। আমার শখের প্রকল্পটি একটি এলইডি-স্ট্রিপটিতে বেশ সুন্দর রঙ প্রদর্শন করতে অস্বাভাবিক কিছু ঘটলে কাউকে মেরে ফেলবে না তবে হার্ট-ফুসফুস মেশিনের ভিতরে থাকা এমসিইউ সম্ভবত এটি করবে।
আমার এলইডি-স্ট্রিপটির জন্য, এটি লক হয়ে গেছে কিনা সে বিষয়ে আমি চিন্তা করি না, আমি কেবল এটি পুনরায় সেট করব। আমি যদি এমসিইউ দ্বারা নিয়ন্ত্রিত হার্ট-ফুসফুসের মেশিনে থাকি তবে এটি লক হয়ে যাওয়া বা পরিচালনা করতে ব্যর্থ হওয়ার পরিণতিগুলি আক্ষরিক জীবন এবং মৃত্যু, তাই উদ্দিষ্ট প্রোগ্রামটি মিঃ-কে প্রদর্শনের সম্ভাবনার সাথে কীভাবে মোকাবিলা করে malloc()
এবং free()
তার মধ্যে প্রশ্নটি বিভক্ত হওয়া উচিত the টুরিংয়ের বিখ্যাত সমস্যা এটি সহজেই ভুলে যাওয়া যায় যে এটি একটি গাণিতিক প্রমাণ এবং নিজেকে বোঝাতে যে কেবলমাত্র আমরা যথেষ্ট বুদ্ধিমান হলেই আমরা গণনার সীমাবদ্ধতার দুর্ঘটনা এড়াতে পারি।
এই প্রশ্নের দুটি স্বীকৃত উত্তর থাকতে হবে, একটি হ্যালটিং প্রব্লেম মুখে পড়ার সময় যারা জ্বলতে বাধ্য হয় তাদের জন্য, এবং একটি অন্য সকলের জন্য। যদিও আরডুইনোর বেশিরভাগ ব্যবহার সম্ভবত মিশন সমালোচনামূলক বা জীবন-মৃত্যুর অ্যাপ্লিকেশন নয়, তবে আপনি কোন এমপিইউ কোডিং করছেন তা বিবেচনা না করেই পার্থক্য এখনও রয়েছে।
না, তবে বরাদ্দ মেমরি মুক্ত করার ক্ষেত্রে এগুলি অবশ্যই খুব সাবধানে ব্যবহার করা উচিত। আমি কখনই বুঝতে পারি নি যে লোকেরা কেন সরাসরি মেমরি পরিচালনা এড়ানো উচিত কারণ এটি এমন একটি মাত্রার অক্ষমতার বোঝায় যা সাধারণত সফ্টওয়্যার বিকাশের সাথে বেমানান হয়।
ড্রোন নিয়ন্ত্রণ করতে আপনি আপনার আরডুইনো ব্যবহার করতে বলুন। আপনার কোডের যে কোনও অংশে যে কোনও ত্রুটি সম্ভাব্যভাবে এটি আকাশ থেকে পড়তে পারে এবং কারও বা কিছুতে আঘাত করতে পারে। অন্য কথায়, যদি কারও কাছে ম্যালোক ব্যবহারের প্রতিযোগিতার অভাব থাকে তবে তারা সম্ভবত কোডিং করা উচিত নয় যেহেতু এমন আরও অনেক অঞ্চল রয়েছে যেখানে ছোট বাগগুলি গুরুতর সমস্যার কারণ হতে পারে।
Malloc দ্বারা সৃষ্ট বাগগুলি ট্র্যাক ডাউন এবং ঠিক করা কি আরও শক্ত? হ্যাঁ, তবে কোডার অংশে ঝুঁকির চেয়ে হতাশার বিষয়টি আরও বেশি। যতদূর ঝুঁকি রয়েছে, আপনার কোডের যে কোনও অংশই ম্যালোকের চেয়ে সমান বা বেশি ঝুঁকিপূর্ণ হতে পারে যদি আপনি সঠিকভাবে কাজটি নিশ্চিত করার জন্য পদক্ষেপ না নেন।