Table of Contents
مقدمة
وتفرض النظم الهندسية المدمجة قيودا صارمة على الذاكرة والسلطة والسلوك في الوقت الحقيقي، وفي هذه البيئات، فإن مبدأ تصميم نمط واحد يقيد طبقة إلى حالة واحدة، ويوفر نقطة عالمية للوصول إلى هذه الخدمات يمكن أن تكون أداة قوية لإدارة الموارد المشتركة للمعدات، وقنوات الاتصال، والدولة على نطاق المنظومة، بيد أن تطبيق هذا النمط بطريقة غير صحيحة يمكن أن يستحدث حشرات فرعية، ويرفع الأداء، ويزيد من استهلاك الطاقة الأولية.
فهم نمط (سينغلتون) في النظم المدمجة
ويضمن نمط " سينغلتون " وجود وجه واحد من الطبقة في أي وقت، وفي النظم المدمجة، يكون هذا الأمر ذا قيمة خاصة بالنسبة لتمثيل المواسير، وسائق الأجهزة، ومديري الموارد الذين يجب أن يحافظوا على وجهة نظر عالمية متسقة.
- UART/USART controllers] - ينبغي أن تدير حالة واحدة فقط النقل وأن تتلقى عوازل.
- ADC (Analog-to-Digital Converter) drivers - multiple clients need to read the same conversion results without duplication.
- وحدات إدارة الدول ] - نقطة واحدة من القرار بشأن دخول نام أو أساليب نشطة.
- Real-time access (RTC) - ينبغي أن يتزامن مع جميع المهام مصدر وحيد للساعة.
- Task schedulers in bare-metal systems — a single cycle or interrupt handler sendes all cooperative tasks.
فبدون وجود شركة " سينغلتون " كثيرا ما يلجأ المطورون إلى المتغيرات العالمية أو الهياكل الثابتة، التي يمكن أن تؤدي إلى عدم اتساق وضع النماذج، ويفرض نمط " سينغلتون " طريقة منضبطة للوصول إلى المعدات، ولكن تنفيذه يجب أن يكيّف مع القيود التي تفرضها المعدات المدمجة: الحيز المحدود للكميات والكميات، وعدم وجود تخصص للذاكرة في بعض السياقات، ووجود عمليات في الوقت الحقيقي.
ومن المفاهيم الخاطئة الشائعة أن نمط العنق هو ببساطة متغير عالمي في فستان فاخر. وفي النظم المدمجة، يجب تنفيذ النمط بضبط دقيق لتوقيت التسلسل الفوري، والسلامة (بما في ذلك السياقات المقاطعية)، والسلوك المدرك للقوة، وتكشف الأقسام التالية أفضل الممارسات التي تفصل بين صوت واحد من واحد خطير.
أفضل الممارسات في مجال التنفيذ
1 - استخدام الاستيعاب الكسول مع توعية السلطة
ولا يعني الاستبدالية الكسولة إنشاء حالة واحدة إلا عندما يتم الوصول إليها أولاً، ويحتفظ هذا النهج بدورات الذاكرة والمعالجة أثناء بدء التشغيل، وهو أمر حاسم في الأجهزة التي تعمل بالبطارية أو التي تُدرَّب على الموارد، وينظر في وجود سائق يُستخدم نادراً في جهاز الاستشعار ذي القوى المنخفضة: فإرجاء إنشاءه إلى أن تصل القيادة المتسلسلة يمكن أن ينقذ عدة مئات من الحركات الجامدة للتجمع وتتجنب الاستهلالية للساعة غير المبرالية.
Example in C (using a static variable):]
// uart_driver.h
typedef struct {
volatile uint32_t *base_addr;
// ... other fields
} UART_HandleTypeDef;
UART_HandleTypeDef* UART_GetInstance(void);
// uart_driver.c
#include "uart_driver.h"
#include "chip_peripherals.h"
UART_HandleTypeDef* UART_GetInstance(void) {
static UART_HandleTypeDef instance;
static int initialized = 0;
if (!initialized) {
instance.base_addr = (uint32_t*)UART1_BASE;
// Perform peripheral-specific configuration
UART_Configure(instance.base_addr);
initialized = 1;
}
return &instance;
}
ملاحظة أن علم الاستهلال هو دليل واضح، في بيئات غير مقصودة، غير أن هناك حاجة إلى تدابير إضافية للوصول إلى الموقع في نفس الوقت (انظر الفرع التالي).
كما أن الاستهلال الكسول يسمح للنظام المختلط بإرجاء تشغيل الطاقة المتولدة عن فترة الهنغارية إلى أن يكون ذلك ضرورياً تماماً، وإذا كان العاصف يُدير جهازاً عالياً (مثلاً، نموذج جهاز إدارة عامة أو وحدة تشغيلية)، فإن إنشاء هذه الحالة يؤدي في وقت لاحق إلى خفض متوسط استهلاك الطاقة، ولكن يكون حذراً: إذا كان الجهاز الذي يُنشأ في حالة توقف نظام واحد يمكن أن يُدخل في وقت توقف.
Link:] For a deep discussion on lazy vs. eager initialization in resource-constrained systems, see ]Embedded Artistry’s sington pattern overview].
2 - ضمان سلامة المستودعات المتعددة المهام والمشتركين في العمليات
وكثيراً ما تخلط النظم المُخلَّصة بين حلقة رئيسية، وروتينات خدمات المقاطعة، وأحياناً نظام تشغيل نظام التشغيل ذي التوقيت الحقيقي، وعندما يُتاح لإحدى الوحدات من سياقات متعددة، يمكن أن تفسد ظروف العرق دولتها - خاصة أثناء الاستهلال الكسول، والمثال الكلاسيكي: تسمى مهمتان في وقت واحد، وكلاهما يرى ، ويُحاولانضان في البداية إلى ضبط الضبط العتاد.
وتختلف السلامة في البيئات المتأصلة عن النظم المكتبية:
- Interrupts cannot use blocking mutexes] — a mutex that might yield the CPU will cause a deadlock if called from an ISR. instead, use critical sections (disable interrupts during the critical region) or lock-free nuclear operations.
- RTOS tasks] - استخدام المتحول أو المنبوذين لحماية وصول (Golton) وإذا كان نظام منع التعذيب يدعم الإرث ذي الأولوية، استخدمه لتجنب الانحراف ذي الأولوية.
- Bare-metal with cooperative scheduling] – if the sington is only accessed from the main cycle, no extra protection is needed, but verify that ISRs never call the sington directly.
Example: Thread-safe sington with CMSIS-RTOS mutex]
#include "cmsis_os2.h"
#include "singleton.h"
static GPIO_TypeDef* instance = NULL;
static osMutexId_t mutex_id;
void Singleton_Init(void) {
mutex_id = osMutexNew(NULL);
}
GPIO_TypeDef* Singleton_GetInstance(void) {
osMutexAcquire(mutex_id, osWaitForever);
if (instance == NULL) {
instance = (GPIO_TypeDef*)GPIOA_BASE;
GPIO_ConfigureInstance(instance);
}
osMutexRelease(mutex_id);
return instance;
}
وفي إطار هذا القانون، يتمثل نهج أكثر أمانا في استخدام العمليات الذرية (مثلاً، في مجلس التعاون الخليجي أو جهاز الدولة خال من القفل، وفي البساطة، يؤدي العديد من نظم الإنتاج عملية الاستبدال المتلهف قبل أن يسمح بالتوقف، ويتجنب التطابق في الوقت المحدد تماماً.
Link:] The ARM CMSIS documentation provides guidelines for thread-safe peripherals: ]CMSIS-Core (ARM) thread safety.
٣ - إبقاء وزن النور العازل - لا توجد هبة، لا مثقفين
وكثيرا ما تكون للنظم المدمجة ذاكرة خبيثة محدودة، كما أن العديد من المشاريع التي تتسم بأهمية بالغة في مجال السلامة تحظر التخصيص الدينامي للاعتمادات بصورة كاملة (قاعدة 21-3 من نظام المعلومات الإدارية) ولذلك ينبغي تخصيص أو وضع العواصف في مناطق مخصصة للذاكرة، كما أن تجنب استخدام أو ، نظرا لأن التجزؤ والأخطاء خارج نطاق الذاكرة يمكن أن تؤدي إلى الفشل.
وينبغي أن يكون البدء في عملية " سينغلتون " أدنى من ذلك:
- تخزين عنوان القاعدة أو التعامل مع الطرف.
- وضع معايير للتشكيل الافتراضي (مثل معدل السود، وفرق ساعات العمل).
- لا تبدأ في الانحرافات التي تستهلك السلطة حتى يسمي العميل صراحة عملية (مثلاً، ).
وفي C++، يمكن أن تنفذوا متغيرا محليا ثابتا في ميار، يضمن إنشاءه مرة واحدة بالضبط (C+11، ثم تضمنوا في وقت لاحق الاستبدالية الثابتة لتصميم الخيوط والسلامة)، غير أنكم تدركون أن الخادم لا يمكن أن يُطلق عليه أبدا إذا استخدم النظام حلقة للرعاية، وأن الأمر الافتراضي القاطع فيما بين وحدات الترجمة يمكن أن يكون أكثر خادعة.
Example of a light weight sington in C++ (Meyer’s):]
class UART {
public:
static UART& getInstance() {
static UART instance; // C++11 thread-safe by default
return instance;
}
private:
UART() {
// lightweight – only store address, no peripheral init
base_ = (uint32_t*)UART1_BASE;
}
uint32_t* base_;
};
ولا يستخدم هذا النمط أي ذاكرة كثيفة ولا يتكبد سوى تكلفة نقطة ثابتة وفحص واحد، ولكن إذا كان المبني يؤدي عمليات تستغرق وقتا طويلا، فينبغي نقله إلى طريقة منفصلة للتمهيد ينادي بها المستخدم صراحة بعد استقرار الطاقة.
4 - تجنب التبعية العلمانية والفصل بين الطول
ونظرا لأن العازبين يوفرون إمكانية الوصول إلى العالم، فإنهم يمكنهم تشجيع تصميم " الجسم " حيث تلتقط وحدات عديدة مباشرة نفس الحالة، مما يجعل من الصعب اختبار الرمز والحفاظ عليه، وأفضل الممارسات هي إدخال الواجهة البينية (أو المؤشر) بين العنبتين في الوحدات التي تحتاج إليها، بدلا من أن تدعوها إلى الداخل، واستخدام نهج حقن الإعالة حيثما أمكن: إنشاء الوحدة النموذجية الأخرى.
على سبيل المثال، بدلا من:
void Sensor_Task(void) {
UART_Transmit(UART_GetInstance(), "Hello");
}
تفضل:
void Sensor_Task(UART_HandleTypeDef* uart) {
UART_Transmit(uart, "Hello");
}
ويفصل هذا الأمر المهمة التي تضطلع بها نقطة الدخول في ولاية سينغلتون، مما يجعل من السهل حقن مركب UART أثناء الاختبار.
الشلالات المشتركة إلى أفويد
Global State Overuse
وغالباً ما يؤدي الاعتماد المفرط على العواقل المخفية إلى تعقيد اختبار الوحدة وإعادة استخدامها، وفي النظم المدمجة، يمكن أن يكون ذلك ضاراً بشكل خاص عندما يدير العُزَّل ولايات السلطة أو يقاطعها ويؤثر على الوحدات الأخرى. Mitigation:] Limit the number of singtons to one or two persyrrger (eg).
Ignoring Power Constraints
إنشاء طائرة واحدة من طراز Unises a high-power peripheral during system start-up يمكن أن تضيع الطاقة في تطبيقات الدورة المنخفضة، على سبيل المثال، ينبغي إنشاء جهاز استقبال GPS واحدتون فقط عندما تكون مهمة الملاحة نشطة. Solution: ] Implement a "shallow" sington that only holds a handle, and provide explicit power-on/powerher
التنظيف السائل المنوي
وفي العديد من النظم المدمجة، يتجاوز نظام " سينغلتون " مرحلة التطبيق - غير أنه إذا كان النظام يدعم التحميل الدينامي (مثلاً، حمولة الأحذية إلى التطبيق) أو إعادة التشكيل في الوقت المناسب، فإن " نينغلتون " قد يحتاج إلى الإفراج عن الموارد.
تحديات القابلية للاختبار
(مثلاً، ]) يجعل من المستحيل استبدال هذه الحالة بضعف الاختبار، وهذا ينتهك المبدأ المفتوح/المغلقة ويثني عن كتابة اختبارات البرمجيات الثابتة. Approach: ] Expose a global change or use a setter for injection during testing (guarded by a [FLT).
Link:] Test-driven development for embedded C is covered in ] experiment-Driven Development for embedded C by James W. Grenning] (provides patterns for decoupling singletons).
أنماط التنفيذ في جيم وجيم ++
C - Static Local with Explicit Locking
يستخدم النمط الموحّد (سي) متغيراً ثابتاً داخل وظيفة محمية بواسطة متحول أو مقطع غير قابل للقطع، وهذا بسيط ولكنه يتطلب معالجة دقيقة للحارس:
// Recommended for single-core with interrupts disabled during init
MyPeripheral* MyPeripheral_GetInstance(void) {
static MyPeripheral inst;
static bool initialized = false;
if (!initialized) {
// Disable interrupts
__disable_irq();
if (!initialized) { // double-check after lock
MyPeripheral_InitHardware(&inst);
initialized = true;
}
__enable_irq();
}
return &inst;
}
ولا يعمل هذا النمط المزدوج المدقق في القفل إلا إذا لم يقم المجمّع بإعادة تخزين المواد وإذا كان الهيكل يضمن قراء/كتب الذرة للعلم (عادة ما يكون هناك رقم متوائم من 32 إلى 20 على متن السفينة ARM Cortex-M).
C++ - Static Local with constexpr and RAII
The C++11 Meyer’s sington is thread-safe by the standard, but be aware that static local variables may have a hidden locking mechanism that consumes stack. For extremely constrained systems, consider a simple static member changing initialised with a constructor that is called early in (eager initialisation). In both cases, rule of thvib:
خاتمة
ولا يزال نمط العنبرة أداة مفيدة في النظم المتأصلة عندما تطبق بالتأديب، إذ إن الالتزام بالتخليص الكسول من خلال التوعية بالطاقة، وتنفيذ السلامة السليمة لنموذج التطابق المستهدف، والحفاظ على بصمة خفيفة، يمكن للمطورين أن يتجنبوا المجازفات التقليدية للدولة العالمية، ونفايات الطاقة، وصعوبة الاختبار، والسؤال دائما عما إذا كان من الضروري حقاً أن يعاد ترتيب هيكل عالمي بسيط مع وظائف واضحة تساعد على تحقيق الهدف نفسه.
Key takeaways:]
- استخدموا الإبدال الكسول لحفظ الموارد لكن تمهيد الطريق بشكل متهور للمسارات الحرجة
- اغلقوا (سينغلتون) مع الآلية المناسبة لجهازكم للإنقاذ أو لقطع هيكلكم؛ لا تغلقوا أبداً داخل معهد الإنقاذ.
- تجنب التخصيص الحاد - أفضل الذاكرة الثابتة.
- تصميم القابلية للاختبار عن طريق حقن واجهة سينغلتون بدلا من الاتصال بأطراف الاتصال العالمية.
- Provide explicit de-initialisation routines for power management and reconfiguration.
Further reading:]
- Wikipedia — singleton pattern]
- Embedded.com - Design patterns for embedded systems]
- C++FAQ – Static initialization order fiasco] (لماذا يمكن أن يكون آكل الأون الأحادية خطراً)