setcontext
تُعدّ دالة setcontext إحدى دوال مكتبة C ( إلى جانب getcontext و makecontext و swapcontext ) المُستخدمة للتحكم في السياق . تُمكّن هذه المجموعة من تنفيذ أنماط تدفق تحكم متقدمة بلغة C، مثل المُكرِّرات والألياف والروتينات الفرعية . يُمكن اعتبارها نسخة مُتقدمة من دالتي setjmp/longjmp ؛ فبينما تسمح الأخيرة بقفزة واحدة غير محلية فقط لأعلى المكدس ، تسمح دالة setcontext بإنشاء خيوط تحكم تعاونية متعددة ، لكل منها مكدسها الخاص.setcontextsetcontext
مواصفة
setcontextتم تحديدها في معيار POSIX .1-2001 ومواصفات يونكس الموحدة ، الإصدار 2، ولكن لا توفرها جميع أنظمة التشغيل الشبيهة بيونكس . وقد ألغى معيار POSIX .1-2004 هذه الوظائف، [ 1 ] وفي معيار POSIX .1-2008 لم تعد مُدرجة، مع الإشارة إلى خيوط POSIX كبديل محتمل.
التعريفات
يتم تعريف الدوال والأنواع المرتبطة بها في ملف رأسucontext.h النظام . ويتضمن ذلك النوع الذي تعمل به جميع الدوال الأربع:ucontext_t
typedef struct { ucontext_t * uc_link ; sigset_t uc_sigmask ; stack_t uc_stack ; mcontext_t uc_mcontext ; ... } ucontext_t ;uc_linkيشير إلى السياق الذي سيتم استئنافه عند انتهاء السياق الحالي، إذا تم إنشاء السياق باستخدام makecontext(سياق ثانوي). uc_sigmaskيُستخدم لتخزين مجموعة الإشارات المحظورة في السياق، uc_stackوهو المكدس الذي يستخدمه السياق. uc_mcontextيخزن حالة التنفيذ ، بما في ذلك جميع السجلات وعلامات وحدة المعالجة المركزية ، ومؤشر التعليمات ، ومؤشر المكدس ؛ وهو نوع مبهم .mcontext_t
الوظائف هي:
intsetcontext(constucontext_t*ucp)- تقوم هذه الدالة بنقل التحكم إلى السياق الموجود في
ucp. ويستمر التنفيذ من النقطة التي تم تخزين السياق عندها فيucp.setcontextولا تُرجع قيمة.
- تقوم هذه الدالة بنقل التحكم إلى السياق الموجود في
intgetcontext(ucontext_t*ucp)- يحفظ هذا التابع السياق الحالي في متغير
ucp. ويعيد قيمة في حالتين محتملتين: إما بعد الاستدعاء الأولي، أو عند انتقال مؤشر ترابط إلى السياق الموجود في المتغيرucpعبر `for`setcontextأو `for`swapcontext.getcontextلا يوفر التابع قيمة إرجاع لتمييز الحالتين (تُستخدم قيمة الإرجاع فقط للإشارة إلى وجود خطأ)، لذا يجب على المبرمج استخدام متغير علامة صريح، والذي يجب ألا يكون متغير سجل، ويجب تعريفه على أنه متقلب لتجنب انتشار الثوابت أو تحسينات المُصرّف الأخرى .
- يحفظ هذا التابع السياق الحالي في متغير
voidmakecontext(ucontext_t*ucp,void(*func)(),intargc,...)makecontextتُهيئ هذه الدالة مسار تحكم بديلًا فيucp، والذي تم تهيئته مسبقًا باستخدامgetcontext. يجب أن يشير العضو إلى مكدس ذي حجم مناسب؛ ويُستخدمucp.uc_stackالثابت عادةً. عند الانتقال إلى باستخدام أو ، سيبدأ التنفيذ من نقطة دخول الدالة المشار إليها بواسطة ، مع الوسائط المحددة. عند انتهاء ، يُعاد التحكم إلى .SIGSTKSZucpsetcontextswapcontextfuncargcfuncucp.uc_link
intswapcontext(ucontext_t*oucp,ucontext_t*ucp)- ينقل التحكم إلى
ucpويحفظ حالة التنفيذ الحالية فيoucp.
- ينقل التحكم إلى
مثال
يوضح المثال أدناه استخدام أداة التكرار setcontext.
#include <stdio.h> #include <stdlib.h> #include <ucontext.h> #include <signal.h>/* السياقات الثلاثة: * (1) main_context1: النقطة في الدالة main التي ستعود إليها الحلقة. * (2) main_context2: النقطة في الدالة main التي سينتقل إليها التحكم من الحلقة * عن طريق تبديل السياقات. * (3) loop_context: النقطة في الحلقة التي سينتقل إليها التحكم من الدالة main * عن طريق تبديل السياقات. */ ucontext_t main_context1 , main_context2 , loop_context ;/* قيمة الإرجاع من المُكرِّر. */ volatile int i_from_iterator ;/* هذه دالة التكرار. يتم استدعاؤها عند أول استدعاء لـ * swapcontext، وتتكرر من 0 إلى 9. يتم حفظ كل قيمة في i_from_iterator، * ثم يُستخدم swapcontext للعودة إلى الحلقة الرئيسية. تقوم الحلقة الرئيسية بطباعة * القيمة وتستدعي swapcontext للتبديل مرة أخرى إلى الدالة. عند الوصول إلى نهاية * الحلقة، تخرج الدالة، وينتقل التنفيذ إلى * السياق الذي يشير إليه main_context1. */ void loop ( ucontext_t * loop_context , ucontext_t * other_context , int * i_from_iterator ) { int i ; for ( i = 0 ; i < 10 ; ++ i ) { /* اكتب عداد الحلقة في موقع إرجاع التكرار. */ * i_from_iterator = i ; /* احفظ سياق الحلقة (هذه النقطة في الكود) في ''loop_context''، * وانتقل إلى other_context. */ swapcontext ( loop_context , other_context ); } /* تنتقل الدالة إلى سياق الاستدعاء ضمنيًا * ''setcontext(&loop_context->uc_link);'' */ } int main ( void ) { /* مكدس دالة التكرار. */ char iterator_stack [ SIGSTKSZ ];/* مؤشر يشير إلى اكتمال عملية التكرار. */ volatile int iterator_finished ;getcontext ( & loop_context ); / * تهيئة سياق المُكرِّر. يشير uc_link إلى main_context1، * وهي النقطة التي يُعاد إليها عند انتهاء المُكرِّر . * / loop_context.uc_link = & main_context1 ; loop_context.uc_stack.ss_sp = iterator_stack ; loop_context.uc_stack.ss_size = sizeof ( iterator_stack ) ;/* املأ متغير loop_context بحيث يبدأ swapcontext الحلقة. يُستخدم التحويل * (void (*)(void)) لتجنب تحذير المُصرّف، ولكنه * غير ذي صلة بسلوك الدالة. */ makecontext ( & loop_context , ( void ( * )( void )) loop , 3 , & loop_context , & main_context2 , & i_from_iterator ); /* امسح علامة الانتهاء. */ iterator_finished = 0 ;/* احفظ السياق الحالي في main_context1. عند انتهاء الحلقة، * سيعود مسار التحكم إلى هذه النقطة. */ getcontext ( & main_context1 ); if ( ! iterator_finished ) { /* اضبط iterator_finished بحيث عند إعادة getcontext السابق عبر uc_link، يكون شرط if أعلاه خاطئًا ولا يُعاد تشغيل المُكرِّر. */ iterator_finished = 1 ; while ( 1 ) { /* احفظ هذه النقطة في main_context2 وانتقل إلى المُكرِّر. * ستبدأ الاستدعاء الأول الحلقة. ستنتقل الاستدعاءات اللاحقة إلى * swapcontext في الحلقة. */ swapcontext ( & main_context2 , & loop_context ); printf ( "%d \n " , i_from_iterator ); } } return 0 ; }ملاحظة: هذا المثال غير صحيح، [ 1 ]makecontext ولكنه قد يعمل كما هو مُصمم له في بعض الحالات. تتطلب الدالة أن تكون المعاملات الإضافية من النوع int، لكن المثال يمرر مؤشرات. لذا، قد يفشل المثال على الأجهزة ذات 64 بت (وتحديدًا معمارية LP64 ). يمكن تجاوز هذه المشكلة بتقسيم وإعادة بناء قيم 64 بت، لكن ذلك يُؤثر سلبًا على الأداء.sizeof(void*)>sizeof(int)
في البنى التي يكون فيها حجم أنواع البيانات من نوع int والمؤشرات متساوياً (مثل x86-32، حيث يكون كلا النوعين 32 بت)، قد يكون من الممكن تمرير المؤشرات كوسيطات إلى دالة makecontext() بعد argc. مع ذلك، لا يُضمن أن يكون هذا الإجراء قابلاً للنقل، وهو غير مُعرَّف وفقاً للمعايير، ولن يعمل على البنى التي تكون فيها المؤشرات أكبر من أعداد int. ومع ذلك، بدءاً من الإصدار 2.8، أدخلت مكتبة glibc بعض التغييرات على دالة makecontext() للسماح بذلك على بعض البنى 64 بت (مثل x86-64).
بالنسبة لسياق get و set، قد يكون السياق الأصغر مفيداً:
#include <stdio.h> #include <ucontext.h> #include <unistd.h>int main ( int argc , const char * argv []){ ucontext_t context ; getcontext ( & context ); puts ( "Hello world" ); sleep ( 1 ); setcontext ( & context ); return 0 ; }هذا يؤدي إلى حلقة لا نهائية لأن السياق يحتوي على عداد البرنامج.
مراجع
روابط خارجية
- سياقات النظام الخامس - دليل مكتبة GNU C
- : الحصول على/تعيين سياق المستخدم الحالي – دليل مبرمج لينكس – وظائف المكتبة من Manned.org
- دليل وظائف مكتبة FreeBSD
- يونكس
- تدفق التحكم
- مكتبات لغة البرمجة C
- الخيوط (الحوسبة)
