التحميل الديناميكي
التحميل الديناميكي هو آلية يمكن من خلالها لبرنامج الكمبيوتر ، في وقت التشغيل ، تحميل مكتبة (أو ملف ثنائي آخر ) في الذاكرة، واسترجاع عناوين الوظائف والمتغيرات الموجودة في المكتبة، وتنفيذ تلك الوظائف أو الوصول إلى تلك المتغيرات، وتفريغ المكتبة من الذاكرة. إنها واحدة من ثلاث آليات يمكن من خلالها لبرنامج الكمبيوتر استخدام بعض البرامج الأخرى داخل البرنامج؛ والآليات الأخرى هي الربط الثابت والربط الديناميكي . على عكس الربط الثابت والربط الديناميكي، يسمح التحميل الديناميكي لبرنامج الكمبيوتر بالبدء في غياب هذه المكتبات، واكتشاف المكتبات المتاحة، واكتساب وظائف إضافية محتملة. [1] [2]
تاريخ
كان التحميل الديناميكي تقنية شائعة لأنظمة تشغيل IBM لنظام System/360 مثل OS/360 ، وخاصةً لبرامج الإدخال والإخراج الفرعية ، ومكتبات وقت التشغيل COBOL و PL/I ، ويستمر استخدامه في أنظمة تشغيل IBM لـ z/Architecture ، مثل z/OS . وفيما يتعلق بمبرمج التطبيق، فإن التحميل شفاف إلى حد كبير، حيث يتم التعامل معه في الغالب بواسطة نظام التشغيل (أو نظام الإدخال والإخراج الفرعي الخاص به). المزايا الرئيسية هي:
- إصلاحات ( تصحيحات ) للأنظمة الفرعية لإصلاح جميع البرامج دفعة واحدة، دون الحاجة إلى إعادة ربطها
- يمكن حماية المكتبات من التعديل غير المصرح به
يستخدم نظام معالجة المعاملات الاستراتيجية لشركة IBM ، CICS (منذ سبعينيات القرن العشرين فصاعدًا)، التحميل الديناميكي على نطاق واسع لكل من نواة النظام وتحميل برامج التطبيقات العادية . يمكن إجراء تصحيحات لبرامج التطبيقات دون اتصال بالإنترنت وتحميل نسخ جديدة من البرامج المتغيرة ديناميكيًا دون الحاجة إلى إعادة تشغيل CICS [3] [4] (الذي يمكنه، وكثيرًا ما يعمل، على مدار الساعة طوال أيام الأسبوع ).
تمت إضافة المكتبات المشتركة إلى يونكس في ثمانينيات القرن العشرين، ولكن في البداية دون القدرة على السماح للبرنامج بتحميل مكتبات إضافية بعد بدء التشغيل. [5]
الاستخدامات
يُستخدم التحميل الديناميكي بشكل متكرر في تنفيذ مكونات البرامج الإضافية . [1] على سبيل المثال، ملفات المكونات الإضافية "الكائن المشترك الديناميكي" لخادم الويب Apache عبارة عن مكتبات يتم تحميلها في وقت التشغيل باستخدام التحميل الديناميكي. [6] يُستخدم التحميل الديناميكي أيضًا في تنفيذ برامج الكمبيوتر حيث قد توفر مكتبات متعددة مختلفة الوظائف المطلوبة وحيث يكون لدى المستخدم خيار تحديد المكتبة أو المكتبات التي سيتم توفيرها.
*.dso
في C/C++
لا تدعم جميع الأنظمة التحميل الديناميكي. توفر أنظمة التشغيل الشبيهة بنظام يونكس مثل macOS و Linux و Solaris التحميل الديناميكي باستخدام مكتبة "dl" الخاصة بلغة البرمجة C. يوفر نظام التشغيل Windows التحميل الديناميكي من خلال واجهة برمجة تطبيقات Windows .
ملخص
| اسم | واجهة برمجة التطبيقات القياسية POSIX/Unix | واجهة برمجة تطبيقات Microsoft Windows |
|---|---|---|
| إدراج ملف الرأس | #include <dlfcn.h>
|
#include <windows.h>
|
| تعريفات للرأس | dl
( |
kernel32.dll
|
| تحميل المكتبة | dlopen
|
LoadLibraryLoadLibraryEx |
| استخراج المحتويات | dlsym
|
GetProcAddress
|
| تفريغ المكتبة | dlclose
|
FreeLibrary
|
تحميل المكتبة
يتم تحميل المكتبة باستخدام LoadLibraryأو LoadLibraryExعلى أنظمة التشغيل Windows وأنظمة التشغيلdlopen الشبيهة بـ Unix . فيما يلي الأمثلة:
معظم أنظمة التشغيل الشبيهة بنظام يونكس (Solaris، Linux، *BSD، وما إلى ذلك)
void * sdl_library = dlopen ( "libSDL.so" ، RTLD_LAZY )؛ if ( sdl_library == NULL ) { // الإبلاغ عن الخطأ ... } else { // استخدام النتيجة في استدعاء dlsym }
ماك
كمكتبة يونكس :
void * sdl_library = dlopen ( "libSDL.dylib" ، RTLD_LAZY )؛ if ( sdl_library == NULL ) { // الإبلاغ عن الخطأ ... } else { // استخدام النتيجة في استدعاء dlsym }
كإطار عمل لنظام التشغيل macOS :
void * sdl_library = dlopen ( "/Library/Frameworks/SDL.framework/SDL" , RTLD_LAZY ); if ( sdl_library == NULL ) { // الإبلاغ عن الخطأ ... } else { // استخدام النتيجة في استدعاء dlsym }
أو إذا كان الإطار أو الحزمة تحتوي على كود Objective-C:
NSBundle * bundle = [ NSBundle bundleWithPath : @"/Library/Plugins/Plugin.bundle" ]; NSError * err = nil ; if ([ bundle loadAndReturnError :& err ]) { // استخدم الفئات والوظائف الموجودة في الحزمة. } else { // تعامل مع الخطأ. }
نوافذ
HMODULE sdl_library = LoadLibrary ( TEXT ( "SDL.dll" )); if ( sdl_library == NULL ) { // الإبلاغ عن الخطأ ... } else { // استخدام النتيجة في استدعاء GetProcAddress }
استخراج محتويات المكتبة
يتم استخراج محتويات مكتبة محملة ديناميكيًا GetProcAddressعلى أنظمة التشغيل Windows وأنظمة التشغيل الشبيهة dlsymبـ Unix .
أنظمة التشغيل الشبيهة بنظام يونكس (Solaris، Linux، *BSD، macOS، إلخ.)
void * initializer = dlsym ( sdl_library , "SDL_Init" ); if ( initializer == NULL ) { // الإبلاغ عن الخطأ ... } else { // تحويل المُهيئ إلى نوعه الصحيح واستخدامه }
على نظام macOS، عند استخدام حزم Objective-C، يمكنك أيضًا:
Class rootClass = [ bundle principalClass ]; // بدلاً من ذلك، يمكن استخدام NSClassFromString() للحصول على فئة حسب الاسم. if ( rootClass ) { id object = [[ rootClass alloc ] init ]; // استخدم الكائن. } else { // أبلغ عن الخطأ. }
نوافذ
FARPROC initializer = GetProcAddress ( sdl_library , "SDL_Init" ); if ( initializer == NULL ) { // report error ... } else { // cast initializer to its right type and use }
تحويل مؤشر وظيفة المكتبة
يجب تحويل
النتيجة dlsym()أو إلى مؤشر من النوع المناسب قبل أن يتم استخدامه.GetProcAddress()
نوافذ
في Windows، يكون التحويل مباشرًا، نظرًا لأن FARPROC هو في الأساس مؤشر وظيفة :
typedef INT_PTR ( * FARPROC ) ( void );
قد يكون هذا الأمر مشكلة عندما يكون من الضروري استرجاع عنوان كائن بدلاً من دالة. ومع ذلك، عادةً ما يرغب المرء في استخراج الدوال على أي حال، لذا لا يمثل هذا مشكلة عادةً.
typedef void ( * sdl_init_function_type )( void ); sdl_init_function_type init_func = ( sdl_init_function_type ) مُهيئ ؛
يونكس (POSIX)
dlsym()وفقًا لمواصفات POSIX، تكون النتيجة عبارة عن voidمؤشر. ومع ذلك، لا يلزم أن يكون لمؤشر الدالة نفس حجم مؤشر كائن البيانات، وبالتالي void*قد لا يكون من السهل تنفيذ تحويل صالح بين النوع والمؤشر إلى دالة على جميع الأنظمة الأساسية.
في أغلب الأنظمة المستخدمة اليوم، تكون مؤشرات الوظائف والكائنات قابلة للتحويل بحكم الأمر الواقع . يوضح مقطع التعليمات البرمجية التالي حلاً بديلاً يسمح بإجراء التحويل على أي حال على العديد من الأنظمة:
typedef void ( * sdl_init_function_type )( void ); sdl_init_function_type init_func = ( sdl_init_function_type ) مُهيئ ؛
ستعطي القطعة أعلاه تحذيرًا بشأن بعض المجمِّعات: warning: dereferencing type-punned pointer will break strict-aliasing rules. الحل البديل الآخر هو:
typedef void ( * sdl_init_function_type )( void ); union { sdl_init_function_type func ; void * obj ; } alias ; alias . obj = initializer ; sdl_init_function_type init_func = alias . func ;
الذي يعطل التحذير حتى إذا كان التعابير الصارمة سارية المفعول. وهذا يستفيد من حقيقة أن القراءة من عضو اتحاد مختلف عن العضو الذي تمت كتابته مؤخرًا (يُسمى " تلاعب بالألفاظ بالنوع ") أمر شائع، ومسموح به صراحةً حتى إذا كان التعابير الصارمة سارية المفعول، بشرط الوصول إلى الذاكرة من خلال نوع الاتحاد مباشرةً. [7] ومع ذلك، هذه ليست الحالة الصارمة هنا، حيث يتم نسخ مؤشر الوظيفة لاستخدامه خارج الاتحاد. لاحظ أن هذه الخدعة قد لا تعمل على الأنظمة الأساسية حيث لا يكون حجم مؤشرات البيانات وحجم مؤشرات الوظيفة هو نفسه.
حل مشكلة مؤشر الوظيفة على أنظمة POSIX
تظل الحقيقة أن أي تحويل بين مؤشرات الوظيفة وكائن البيانات يجب اعتباره امتدادًا للتنفيذ (غير قابل للنقل بطبيعته)، وأنه لا توجد طريقة "صحيحة" للتحويل المباشر، حيث تتناقض معايير POSIX وISO في هذا الصدد مع بعضها البعض.
وبسبب هذه المشكلة، dlsym()ذكرت وثائق POSIX الخاصة بالإصدار 6 القديم أن "الإصدار المستقبلي قد يضيف وظيفة جديدة لإرجاع مؤشرات الوظائف، أو قد يتم إيقاف استخدام الواجهة الحالية لصالح وظيفتين جديدتين: واحدة تقوم بإرجاع مؤشرات البيانات والأخرى تقوم بإرجاع مؤشرات الوظائف". [8]
بالنسبة للإصدار اللاحق من المعيار (الإصدار 7، 2008)، تمت مناقشة المشكلة وكان الاستنتاج هو أن مؤشرات الوظيفة يجب أن تكون قابلة للتحويل إلى void*التوافق مع POSIX. [8] وهذا يتطلب من صانعي المترجمين تنفيذ عملية صب عمل لهذه الحالة.
إذا كان من الممكن تغيير محتويات المكتبة (أي في حالة المكتبة المخصصة)، فبالإضافة إلى الوظيفة نفسها، يمكن تصدير مؤشر إليها. ونظرًا لأن المؤشر إلى مؤشر الوظيفة هو في حد ذاته مؤشر كائن، فيمكن دائمًا استرداد هذا المؤشر بشكل قانوني عن طريق استدعاء dlsym()والتحويل اللاحق. ومع ذلك، يتطلب هذا النهج الحفاظ على مؤشرات منفصلة لجميع الوظائف التي سيتم استخدامها خارجيًا، وعادةً ما تكون الفوائد صغيرة.
تفريغ المكتبة
يؤدي تحميل مكتبة إلى تخصيص الذاكرة؛ ويجب إلغاء تخصيص المكتبة لتجنب تسرب الذاكرة . بالإضافة إلى ذلك، قد يؤدي الفشل في إلغاء تحميل مكتبة إلى منع عمليات نظام الملفات على الملف الذي يحتوي على المكتبة. يتم إنجاز إلغاء تحميل المكتبة باستخدام على FreeLibraryأنظمة التشغيل Windows وأنظمة التشغيلdlclose الشبيهة بـ Unix . ومع ذلك، قد يؤدي إلغاء تحميل مكتبة DLL إلى تعطل البرنامج إذا كانت الكائنات في التطبيق الرئيسي تشير إلى الذاكرة المخصصة داخل مكتبة DLL. على سبيل المثال، إذا قدمت مكتبة DLL فئة جديدة وتم إغلاق مكتبة DLL، فمن المحتمل أن تتسبب المزيد من العمليات على مثيلات تلك الفئة من التطبيق الرئيسي في انتهاك الوصول إلى الذاكرة. وبالمثل، إذا قدمت مكتبة DLL وظيفة مصنع لإنشاء مثيلات للفئات المحملة ديناميكيًا، فإن استدعاء أو إلغاء الرجوع إلى تلك الوظيفة بعد إغلاق مكتبة DLL يؤدي إلى سلوك غير محدد.
أنظمة التشغيل الشبيهة بنظام يونكس (Solaris، Linux، *BSD، macOS، إلخ.)
dlclose ( مكتبة sdl )؛
نوافذ
المكتبة المجانية ( sdl_library );
مكتبة خاصة
تسمح تنفيذات التحميل الديناميكي على أنظمة التشغيل الشبيهة بنظام Unix و Windows للمبرمجين باستخراج الرموز من العملية الجارية حاليًا.
تسمح أنظمة التشغيل الشبيهة بنظام يونكس للمبرمجين بالوصول إلى جدول الرموز العالمي، والذي يتضمن كل من الملف التنفيذي الرئيسي والمكتبات الديناميكية المحملة لاحقًا.
يتيح Windows للمبرمجين الوصول إلى الرموز التي تم تصديرها بواسطة الملف التنفيذي الرئيسي. لا يستخدم Windows جدول رموز عالميًا ولا يحتوي على واجهة برمجة تطبيقات للبحث عبر وحدات متعددة للعثور على رمز حسب الاسم.
أنظمة التشغيل الشبيهة بنظام يونكس (Solaris، Linux، *BSD، macOS، إلخ.)
void * هذه العملية = dlopen ( NULL ، 0 )؛
نوافذ
HMODULE هذه العملية = GetModuleHandle ( NULL )؛
HMODULE هذه العملية مرة أخرى ؛ GetModuleHandleEx ( 0 ، 0 ، & هذه العملية مرة أخرى )؛
في جافا
في لغة برمجة Java ، يمكن تحميل الفئاتClassLoader ديناميكيًا باستخدام الكائن. على سبيل المثال:
نوع الفئة = ClassLoader . getSystemClassLoader (). loadClass ( الاسم )؛ الكائن obj = النوع . newInstance ()؛
توفر آلية الانعكاس أيضًا وسيلة لتحميل فئة إذا لم يتم تحميلها بالفعل. وهي تستخدم محمل الفئة الحالية:
نوع الفصل = Class.forName ( name ) ; الكائن obj = type.newInstance ( ) ;
ومع ذلك، لا توجد طريقة بسيطة لتفريغ فئة بطريقة محكومة. لا يمكن تفريغ الفئات المحملة إلا بطريقة محكومة، أي عندما يريد المبرمج حدوث ذلك، إذا لم يكن محمل الفئة المستخدم لتحميل الفئة هو محمل فئة النظام، وتم تفريغه هو نفسه. عند القيام بذلك، يجب مراعاة تفاصيل مختلفة لضمان تفريغ الفئة حقًا. وهذا يجعل تفريغ الفئات أمرًا مملًا.
لقد تغيرت عملية تفريغ الفئات الضمنية، أي بطريقة غير خاضعة للتحكم من قِبل جامع القمامة، عدة مرات في Java. فحتى Java 1.2، كان جامع القمامة قادرًا على تفريغ فئة ما كلما شعر أنها بحاجة إلى المساحة، بغض النظر عن محمل الفئة المستخدم لتحميل الفئة. بدءًا من Java 1.2، لم يتم تفريغ الفئات المحملة عبر محمل فئة النظام مطلقًا والفئات المحملة عبر محملات فئات أخرى فقط عندما يتم تفريغ محمل الفئة الآخر هذا. بدءًا من Java 6، يمكن أن تحتوي الفئات على علامة داخلية تشير إلى جامع القمامة أنه يمكن تفريغها إذا رغب جامع القمامة في القيام بذلك، بغض النظر عن محمل الفئة المستخدم لتحميل الفئة. جامع القمامة حر في تجاهل هذا التلميح.
على نحو مماثل، يتم تحميل المكتبات التي تنفذ الأساليب الأصلية ديناميكيًا باستخدام System.loadLibraryالطريقة. لا توجد System.unloadLibraryطريقة.
المنصات بدون تحميل ديناميكي
على الرغم من إصداره في الثمانينيات من خلال يونكس وويندوز، إلا أن بعض الأنظمة اختارت عدم إضافة التحميل الديناميكي أو حتى إزالته. على سبيل المثال، تتجنب Plan 9 من Bell Labs وخليفتها 9front الارتباط الديناميكي عمدًا، لأنهما يعتبرانه "ضارًا". [9] لغة برمجة Go ، من قبل بعض نفس مطوري Plan 9، لم تدعم الارتباط الديناميكي أيضًا، ولكن تحميل المكونات الإضافية متاح منذ Go 1.8 (فبراير 2017). يتم ربط وقت تشغيل Go وأي وظائف مكتبة بشكل ثابت في الملف الثنائي المجمّع. [10]
انظر أيضا
- تجميع النظام والذهاب إليه
- جحيم DLL
- الربط المباشر
- الربط الديناميكي (الحوسبة)
- الإرسال الديناميكي
- مكتبة ديناميكية
- رابط ديناميكي
- مكتبة الارتباط الديناميكي
- فليكس أو إس
- رابط جنو
- الذهب (الرابط)
- التحميل الكسول
- المكتبة (الحوسبة)
- الرابط (الحوسبة)
- محمل (حوسبة)
- زخرفة الاسم
- الربط المسبق
- الربط المسبق
- النقل (علوم الكمبيوتر)
- جدول النقل
- امتداد النظام المقيم (RSX)
- مكتبة ثابتة
- برنامج إنهاء الإقامة والبقاء (TSR)
مراجع
- ^ ab Autoconf وAutomake وLibtool: التحميل الديناميكي
- ^ "Linux4U: ELF Dynamic Loading". مؤرشف من الأصل في 2011-03-11 . تم الاسترجاع في 2007-12-31 .
- ^ "استخدام الإجراءات التي توفرها CICS لتثبيت برامج التطبيق".
- ^ "فشل طلب IBM CEMT NEWCOPY أو PHASEIN مع NOT FOR HOLD PROG - الولايات المتحدة". 2013-03-15.
- ^ Ho, W. Wilson; Olsson, Ronald A. (1991). "نهج للربط الديناميكي الحقيقي". البرمجيات: الممارسة والخبرة . 21 (4): 375–390. CiteSeerX 10.1.1.37.933 . doi :10.1002/spe.4380210404. S2CID 9422227.
- ^ "دعم كائنات Apache 1.3 المشتركة الديناميكية (DSO)". مؤرشف من الأصل في 2011-04-22 . تم الاسترجاع في 2007-12-31 .
- ^ GCC 4.3.2 خيارات التحسين: -fstrict-aliasing
- ^ توثيق POSIX على dlopen() (العددان 6 و7).
- ^ "الربط الديناميكي". cat-v.org . 9front . تم الاسترجاع في 2014-12-22 .
- ^ "الأسئلة الشائعة".
قراءة إضافية
- Silberschatz, Abraham; Galvin, Peter Baer; Gagne, Greg (2005). "الفصل 8.1.4 "التحميل الديناميكي" والفصل 8.1.5 "الربط الديناميكي والمكتبات المشتركة"". مفاهيم نظام التشغيل . J. Wiley & Sons . ISBN 978-0-471-69466-3.
روابط خارجية
- روابط عامة
- التحميل الديناميكي على Linux4U
- دعم الكائن المشترك الديناميكي (DSO) بواسطة Apache
- الارتباط الديناميكي في لغة C++ من خلال المثال
- مثال على تحميل المكتبة الديناميكية (مثال عملي كامل ولكنه موجز)
- موضوعات برمجة المكتبة الديناميكية من Apple Developer Connection (موجهة لنظام macOS)
- واجهة برمجة تطبيقات يونكس C/C++:
- دلبين
- دلسيما
- دل كلوز
- واجهة برمجة تطبيقات Windows C/C++:
- تحميل المكتبة
- الحصول على عنوان الإحتياج
- المكتبة المجانية
- ملفات DLL المحملة بالتأخير
- واجهة برمجة تطبيقات جافا:
- محمل الفئة
- فصل
