برمجة موثوقة في سياق اللغات. الجزء 2 - المتحدون

الجزء الأول مع المتطلبات الوظيفية هنا .

تمت المطالبة بلغات البرمجة مع التركيز على الموثوقية.

أبجديًا - Active Oberon ، Ada ، BetterC ، IEC 61131-3 ST ، Safe-C.

في وقت واحد ، لا يمثل إخلاء المسئولية (عذرًا) بأي حال من الأحوال حملة "الكل على الجانب الأيسر" ، والمراجعة أكاديمية إلى حد ما - قد لا تحتوي اللغة على بيئة تطوير حديثة مدعومة بشكل نشط فحسب ، بل مترجم لمنصة التداول الخاصة بك.

من ناحية أخرى ، بالنسبة إلى اللغات المعنية ، يوجد مترجمون مفتوحون المصدر ، ومع المستوى الحالي لتطوير البرمجيات - مع الاهتمام ، لا يسمح لك بناء الجملة المعقد للغاية بإنشاء مترجم شخصي والاندماج في نوع من Eclipse مع الإضاءة الخلفية والمحلل اللغوي.

كمؤشر على وضوح اللغة ، اخترت تنفيذ مهمة Dijkstra المعروفة متعددة مؤشرات الترابط حول فلاسفة الطعام. يتم التنفيذ في الكتب المدرسية على اللغة وعلى المنتديات ، مما سهل عملي - يبقى فقط للتكيف. على سبيل المثال ، تحتوي مقالة حديثة خاصة بـ C ++ الحديثة على تطبيق C ++ 17 للمقارنة.

أكتيف أوبيرون (2004)


تم إنشاؤه مع التركيز على تجربة Pascal، Modula، Oberons السابقة منذ 1988، Java، C #، Ada ، بالإضافة إلى الخبرة العملية في التطبيق. يحتوي على تطبيق على شكل OS A2 ، والذي يمكن أن يكون بمثابة وقت تشغيل أعلى من * nix أو Windows. المصادر A2 ومترجم الارتباط .

يوجد أيضًا مشروع Oberon2 to C Compiler (OOC) غير مرتبط ببيئة Oberon. هذه لهجة مختلفة قليلاً ، يتم وصف الاختلافات أدناه.

الميزة الرئيسية لأوبرون هي الإيجاز الاستثنائي للمواصفات. هذه هي 16 صفحة في قاعدة Oberon-2 plus و 23 صفحة على امتداد Active متعدد الخيوط.

بناء جملة بسيط وواضح يستثني الأخطاء الواضحة.

معرفات حساسة لحالة الأحرف.

OOP مع كائنات على الكومة مع جامع البيانات المهملة (GC).

إنه يختلف عن سابقاتها في بناء جملة OOP الأكثر شيوعًا في شكل Instance.Method (اعتاد أن يكون الأسلوب (مثيل)) ودعم تعدد العمليات مع بدائل التزامن.
لا يوجد إرسال ديناميكي في تنفيذ OOP ، والذي يمكن أن يؤدي بسهولة إلى موقف - لقد نسوا إضافة معالجة لنوع جديد.

يمكن إعطاء التدفقات الأولوية و / عالية الوقت الحقيقي لا يتم مقاطعة من قبل GC. سلاسل في شكل صفائف UTF-8.

يوفر Rantime (نظام Oberon) فرصًا مثيرة للاهتمام لإعادة تشغيل إجراء / وحدة نمطية / مؤشر ترابط فاشل في حالة حدوث خطأ في وقت التشغيل - معالجة الذاكرة ، أو على سبيل المثال ، تجاوز عدد صحيح.

العيب هو عدم وجود RAII ، ومعالجة الأخطاء مريحة - كل ذلك من خلال رموز الإرجاع ، باستثناء الخيار أدناه.

أوبيرون -2 OOC


هو أكثر ملاءمة للتجارب ، حيث أن Oberon لا يتطلب نظام تشغيل - يتم تجميعه في ANSI C ولا توجد مشكلات في التشغيل البيني. الاختلافات من الإصدار النشط - لا توجد لغة متعددة مؤشرات الترابط المضمنة - بدلاً من ذلك ، هناك وحدة نمطية للعمل مع PThreads ، ولكن هناك UTF16 والوحدات الهرمية والوحدة النمطية للنظام للعمل مع الاستثناءات.

الوحدة 3


هناك أيضًا قريب من فرع تطوير مختلف قليلاً في شكل Modula-3. تم إنشاؤه على أساس أوبرون بدلا من آدا متخلفة. التنفيذ هنا .

بالمقارنة مع Active Oberon ، تتم إضافة الأدوية العامة والاستثناءات ، وهناك مكتبات للعمل العملي مع Unicode و GUI وحتى Postgress. التكامل المبسط مع C. دلالات multithreading الأخرى. RAII كـ WITH (يشبه الاستخدام في C #).

ولكن يبدو أن تطوير Modula 3 توقف في عام 2010.

إخلاء المسؤولية. بعد أن قمت بتشغيل WinAOS ، واجهتُ TRAPs (الملقب بـ abort / stacktrace أو خطأ في وقت التشغيل) من اللون الأزرق - حتى مدير المهام لا يعمل بشكل صحيح ، وعلى الرغم من أن تعطل النظام / وقت التشغيل - ولكن التطبيق فقط ، كان لدي شكوك معينة في أن الموثوقية يتم تحديدها من خلال اللغة البرمجة = (

أيضا ، AOC ما يكفي من الاكتفاء الذاتي ، مع نهجها في التنمية.

مصدر لتناول الطعام الفلاسفة
MODULE Philo; (* Dining Philosophers Example from Active Oberon Language Report by Patrik Reali *) (* Adapted for running in AOS by Siemargl *) IMPORT Semaphores := Example8, Out; CONST NofPhilo = 5; (* number of philosophers *) VAR fork: ARRAY NofPhilo OF Semaphores.Semaphore; i: LONGINT; TYPE Philosopher = OBJECT VAR first, second: LONGINT; (* forks used by this philosopher *) PROCEDURE & Init(id: LONGINT); BEGIN IF id # NofPhilo-1 THEN first := id; second := (id+1) ELSE first := 0; second := NofPhilo-1 END END Init; PROCEDURE Think; (* Need lock console output *) BEGIN {EXCLUSIVE} Out.Int(first); Out.String(".... Think...."); Out.Ln; END Think; PROCEDURE Eat; BEGIN {EXCLUSIVE} Out.Int(first); Out.String(".... Eat...."); Out.Ln; END Eat; BEGIN {ACTIVE} LOOP Think; fork[first].P; fork[second].P; Eat; fork[first].V; fork[second].V END END Philosopher; VAR philo: ARRAY NofPhilo OF Philosopher; BEGIN FOR i := 0 TO NofPhilo DO NEW(fork[i], INTEGER(i)); NEW(philo[i], i); END; END Philo. Philo.Philo1 ~ 

Ada (1980 ، آخر معيار صالح 2016)


في الواقع ، للوهلة الأولى هناك كل ما أود.

وأكثر من ذلك بقليل - هناك أرقام مع حسابات الفاصلة العائمة بالضبط. على سبيل المثال ، هناك جدولة مؤشر ترابط الوقت الفعلي ، وتبادل مؤشر الترابط عبر مجموعة فرعية التحقق رسميًا من اللغة SPARK. وأكثر من ذلك بكثير.

أعتقد أنه إذا كانت موثوقية Ada بحاجة إلى قرن لعن ، فسيتم إرفاقها بتعليمات للاتصال في موقف صعب =)

التنفيذ - GNUTaya Ada ، يعمل على تطوير معايير ISO / IEC.

يوفر المعيار التنفيذ مع GC ، ولكن بالنسبة للخيارات المترجمة ، فإنه لا يتم تطبيقه في الغالب. مطلوب إدارة الذاكرة اليدوية - وهنا أخطاء مبرمج ممكن. ومع ذلك ، يتم توجيه اللغة نحو استخدام المكدس الافتراضي وهناك مفهوم الأنواع المدارة مع المدمرات. يمكنك أيضًا تحديد تطبيق GC أو الإطلاق التلقائي أو العد المرجعي لكل نوع بيانات.

يحتوي Ada Reference Manual 2012 على 950 صفحة.

عيب آدا ، إلى جانب التعقيد ، هو لفظته المفرطة ، والتي ، مع ذلك ، تم تصميمها من أجل سهولة القراءة. نظرًا لخصوصية نموذج أمان اللغة ، فإن التكامل مع المكتبات الأجنبية أمر صعب.

يحتوي موقع Ada-ru على مقالة ترجمة ترجمة جيدة - الرابط الأول.

مصدر لتناول الطعام الفلاسفة
 -- Code from https://rosettacode.org/wiki/Dining_philosophers#Ordered_mutexes -- ADA95 compatible so can run in ideone.com with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random; with Ada.Text_IO; use Ada.Text_IO; procedure Test_Dining_Philosophers is type Philosopher is (Aristotle, Kant, Spinoza, Marx, Russel); protected type Fork is entry Grab; procedure Put_Down; private Seized : Boolean := False; end Fork; protected body Fork is entry Grab when not Seized is begin Seized := True; end Grab; procedure Put_Down is begin Seized := False; end Put_Down; end Fork; Life_Span : constant := 20; -- In his life a philosopher eats 20 times task type Person (ID : Philosopher; First, Second : not null access Fork); task body Person is Dice : Generator; begin Reset (Dice); for Life_Cycle in 1..Life_Span loop Put_Line (Philosopher'Image (ID) & " is thinking"); delay Duration (Random (Dice) * 0.100); Put_Line (Philosopher'Image (ID) & " is hungry"); First.Grab; Second.Grab; Put_Line (Philosopher'Image (ID) & " is eating"); delay Duration (Random (Dice) * 0.100); Second.Put_Down; First.Put_Down; end loop; Put_Line (Philosopher'Image (ID) & " is leaving"); end Person; Forks : array (1..5) of aliased Fork; -- Forks for hungry philosophers -- Start philosophers Ph_1 : Person (Aristotle, Forks (1)'Access, Forks (2)'Access); Ph_2 : Person (Kant, Forks (2)'Access, Forks (3)'Access); Ph_3 : Person (Spinoza, Forks (3)'Access, Forks (4)'Access); Ph_4 : Person (Marx, Forks (4)'Access, Forks (5)'Access); Ph_5 : Person (Russel, Forks (1)'Access, Forks (5)'Access); begin null; -- Nothing to do in the main task, just sit and behold end Test_Dining_Philosophers; 


BetterC (dlang subset 2017، D الأصلي - 2001، D 2.0 - 2007)


تنفيذ أحدث من النظر فيها. الوصف الكامل للغة طويل جدًا - 649 صفحة - انظر الموقع الأصلي .

في الواقع ، هذه هي اللغة D ، ولكن مع قيود مع التبديل -betterC. لماذا هكذا؟

لأن المكتبة القياسية D هي Phobos ، التي طورتها Alexandrescu واتضح أنها ماكرة للغاية ، مبنية بالكامل على قوالب. المفتاح لهذا الموضوع هو أن Phobos لا يمكن السيطرة عليها من حيث استهلاك الذاكرة.

أهم الأشياء التي تضيع في وضع BetterC هي تعدد مؤشرات الترابط ، و GC ، والخيوط ، والفئات (تظل الهياكل - قريبة من وظائفها - فقط على المكدس) والاستثناءات (RAII وتبقى تجربة النهاية).

من الممكن ، مع ذلك ، كتابة جزء من البرنامج بالكامل D ، والجزء المهم في D-BetterC. هناك أيضًا وظيفة سمة نظام للتحكم في عدم استخدام التأثيرات الخطيرة: pure safenogc.

تبرير النظام من خالق اللغة.

ثم الضغط - ما هو مقطوع وما تبقى المتاحة.

يتم تضمين السلاسل في Phobos - ومحاولات استخدامها في BetterC تؤدي إلى أخطاء جسيمة من إنشاء قوالب في العمليات الأولية مثل إخراج سلسلة إلى وحدة التحكم أو تسلسل. وفي وضع D الكامل ، تكون الخطوط الموجودة على الكومة غير قابلة للتغيير أيضًا ، وبالتالي تؤدي العمليات معهم إلى فوضى الذاكرة.

اضطررت إلى تلبية شكاوى حول الأخطاء في المترجم عدة مرات. التي ، مع ذلك ، ليس من المستغرب وجود لغة تتنافس في التعقيد مع C ++. عند إعداد المقالة ، كان علي أيضًا مواجهة 4 أخطاء - نشأ اثنان عند محاولة إنشاء dlangide مع مترجم جديد وزوجين عند نقل مشكلة الفيلسوف (على سبيل المثال ، تعطل عند استخدام startthreadex).

لم يظهر الوضع إلا مؤخرًا وتخرج الأخطاء التي تسببها تقييد وضع BetterC بالفعل في مرحلة الربط. للتعرف على هذا مسبقًا ، ما هي ميزات اللغة التي يتم قصها تمامًا - غالبًا ما يتعين عليها القيام بذلك بشكل مباشر.

مصدر لتناول الطعام الفلاسفة
 // compile dmd -betterC import core.sys.windows.windows; import core.stdc.stdio; import core.stdc.stdlib : rand; //import std.typecons; // -impossible ( //import std.string; - impossible extern (Windows) alias btex_fptr = void function(void*) /*nothrow*/; //extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*) nothrow; /* Dining Philosophers example for a habr.com * by Siemargl, 2019 * BetterC variant. Compile >dmd -betterC Philo_BetterC.d */ extern (C) uintptr_t _beginthread(btex_fptr, uint stack_size, void *arglist) nothrow; alias HANDLE uintptr_t; alias HANDLE Fork; const philocount = 5; const cycles = 20; HANDLE[philocount] forks; struct Philosopher { const(char)* name; Fork left, right; HANDLE lifethread; } Philosopher[philocount] philos; extern (Windows) void PhilosopherLifeCycle(void* data) nothrow { Philosopher* philo = cast(Philosopher*)data; for (int age = 0; age++ < cycles;) { printf("%s is thinking\n", philo.name); Sleep(rand() % 100); printf("%s is hungry\n", philo.name); WaitForSingleObject(philo.left, INFINITE); WaitForSingleObject(philo.right, INFINITE); printf("%s is eating\n", philo.name); Sleep(rand() % 100); ReleaseMutex(philo.right); ReleaseMutex(philo.left); } printf("%s is leaving\n", philo.name); } extern (C) int main() { version(Windows){} else { static assert(false, "OS not supported"); } philos[0] = Philosopher ("Aristotlet".ptr, forks[0], forks[1], null); philos[1] = Philosopher ("Kant".ptr, forks[1], forks[2], null); philos[2] = Philosopher ("Spinoza".ptr, forks[2], forks[3], null); philos[3] = Philosopher ("Marx".ptr, forks[3], forks[4], null); philos[4] = Philosopher ("Russel".ptr, forks[0], forks[4], null); foreach(ref f; forks) { f = CreateMutex(null, false, null); assert(f); } foreach(ref ph; philos) { ph.lifethread = _beginthread(&PhilosopherLifeCycle, 0, &ph); assert(ph.lifethread); } foreach(ref ph; philos) WaitForSingleObject(ph.lifethread, INFINITE); // Close thread and mutex handles for( auto i = 0; i < philocount; i++ ) { CloseHandle(philos[i].lifethread); CloseHandle(forks[i]); } return 0; } 


للمقارنة ، المصدر الكامل

على الوردة يمكنك أيضًا رؤية خيارات للغات أخرى.

IEC 61131-3 ST (1993 ، أحدث المعايير 2013)


لغة برمجة متخصصة للميكروكونترولر. يتضمن المعيار 5 خيارات برمجة ، لكن كتابة تطبيق على سبيل المثال في منطق السلم لا يزال مغامرة. لذلك ، نحن نركز على خيار واحد - نص منظم.
نص معيار GOST R IEC 61131-3-2016 - 230 صفحة.

هناك تطبيقات لجهاز الكمبيوتر / x86 و ARM - والتطبيقات التجارية ، وأشهرها هي CODESYS (غالبًا ما يتم ترجمتها أيضًا بأسماء مختلفة) والبث المفتوح - Beremiz - يتم البث عبر C.

نظرًا لوجود تكامل مع C ، فمن الممكن تمامًا توصيل المكتبات الضرورية للبرمجة التطبيقية. من ناحية أخرى ، من المقبول في هذا المجال أن المنطق يدور بشكل منفصل ويعمل فقط كخادم بيانات لبرنامج أو نظام آخر - واجهة مع مشغل أو DBMS يمكن كتابتها بالفعل على أي شيء - دون متطلبات الوقت الفعلي أو حتى أي مؤقت بشكل عام ...

لقد ظهرت برمجة متعددة مؤشرات الترابط لبرنامج مستخدم مؤخرًا نسبيًا - في ميكروكنترولر ، لم يكن هذا ضروريًا من قبل.

صب النوع في الغالب صريح فقط (مرتاح بأحدث المعايير). لكن التحكم في التدفق يعتمد على التنفيذ.

في الإصدار الأخير من المعيار ، ظهر OOP. تتم معالجة الأخطاء بواسطة معالجات المقاطعة المخصصة.

يمكننا القول أنه لا يوجد تخصيص ذاكرة ديناميكي للمستخدم. حدث هذا تاريخيا - كمية البيانات التي تتم معالجتها بواسطة متحكم دائمًا ما تكون محدودة من الأعلى.

المصدر (لم يتم التحقق منه)
 (* Dining Philosophers example for a habr.com * by Siemargl, 2019 * ISO61131 ST language variant. Must be specialized 4 ur PLC * ) CONFIGURATION PLC_1 VAR_GLOBAL Forks : USINT; Philo_1: Philosopher; (* Instance block - static vars *) Philo_2: Philosopher; Philo_3: Philosopher; Philo_4: Philosopher; Philo_5: Philosopher; END_VAR RESOURCE Station_1 ON CPU_1 TASK Task_1 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_2 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_3 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_4 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_5 (INTERVAL := T#100MS, PRIORITY := 1); PROGRAM Life_1 WITH Task_1: Philo_1(Name := 'Kant', 0, 1, Forks); PROGRAM Life2 WITH Task_2: Philo_2(Name := 'Aristotel', 1, 2, Forks); PROGRAM Life3 WITH Task_3: Philo_3(Name := 'Spinoza', 2, 3, Forks); PROGRAM Life4 WITH Task_4: Philo_4(Name := 'Marx', 3, 4, Forks); PROGRAM Life5 WITH Task_5: Philo_5(Name := 'Russel', 4, 0, Forks); END_RESOURCE END_CONFIGURATION FUNCTION_BLOCK Philosopher; USING SysCpuHandling.library; VAR_INPUT Name: STRING; Left: UINT; Right: UINT; END_VAR VAR_IN_OUT Forks: USINT; END_VAR VAR Thinking: BOOL := TRUE; (* States *) Hungry: BOOL; Eating: BOOL; HaveLeftFork: BOOL; TmThink: TON; TmEating: TON; END_VAR TmThink(In := Thinking; PT := T#3s); TmEating(In := Eating; PT := T#5s); IF Thinking THEN (* Just waiting Timer *) Thinking := NOT TmThink.Q; Hungry := TmThink.Q; ELSIF Hungry (* Try Atomic Lock Forks *) IF HaveLeftFork IF SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Right, bSet := 1) = ERR_OK THEN Hungry := FALSE; Eating := TRUE; ELSE RETURN; END_IF ELSIF IF SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Left, bSet := 1) = ERR_OK THEN HaveLeftFork := TRUE; ELSE RETURN; END_IF END_IF ELSIF Eating (* Waiting Timer, then lay forks *) IF TmEating.Q THEN Thinking := TRUE; Eating := FALSE; HaveLeftFork := FALSE; SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Right, bSet := 0); SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Left, bSet := 0); END_IF END_IF END_FUNCTION_BLOCK 


Safe-C (2011)


C التجريبية مع إزالة الرقائق الخطرة مع إضافة نمطية و multithreading. موقع المشروع
وصف حوالي 103 صفحات. إذا كنت تسليط الضوء على الاختلافات من C - القليل جدا ، حوالي 10 .

يعد العمل مع المصفوفات والمؤشرات ذاكرة آمنة وديناميكية مع حساب المرجع التلقائي - مع اختبارات إصدار مزدوجة وروابط متدلية.

تحتوي المكتبة القياسية على مجموعة بسيطة من وظائف واجهة المستخدم الرسومية ، وخاصية مؤشرات الترابط ، ووظائف الشبكة (بما في ذلك خادم http).

ولكن - هذا التطبيق هو فقط لنظام التشغيل Windows x86. على الرغم من أن رمز المترجم والمكتبة مفتوح.

كجزء من مهمة بحثية أخرى ، قمت بتخطيط تخطيط خادم ويب يجمع البيانات من أجهزة استشعار IoT: وحدة تنفيذية بسعة 75 كيلوبايت ومجموعة ذاكرة جزئية <1 ميجابايت.

مصدر لتناول الطعام الفلاسفة
 /* Dining Philosophers example for a habr.com * by Siemargl, 2019 * Safe-C variant. Compile >mk.exe philosafec.c */ from std use console, thread, random; enum philos (ushort) { Aristotle, Kant, Spinoza, Marx, Russell, }; const int cycles = 10; const ushort NUM = 5; uint lived = NUM; packed struct philosopher // 32-bit { philos name; byte left, right; } philosopher philo_body[NUM]; SHARED_OBJECT forks[NUM]; void philosopher_life(philosopher philo) { int age; for (age = 0; age++ < cycles; ) { printf("%s is thinking\n", philo.name'string); delay((uint)rnd(1, 100)); printf("%s is hungry\n", philo.name'string); enter_shared_object(ref forks[philo.left]); enter_shared_object(ref forks[philo.right]); printf("%s is eating\n", philo.name'string); delay((uint)rnd(1, 100)); leave_shared_object(ref forks[philo.right]); leave_shared_object(ref forks[philo.left]); } printf("%s is leaving\n", philo.name'string); InterlockedExchange(ref lived, lived-1); } void main() { philos i; assert philosopher'size == 4; philo_body[0] = {Aristotle, 0, 1}; philo_body[1] = {Kant, 1, 2}; philo_body[2] = {Spinoza, 2, 3}; philo_body[3] = {Marx, 3, 4}; philo_body[4] = {Russell, 0, 4}; for (i = philos'first; i <= philos'last; i++) { assert run philosopher_life(philo_body[(uint)i]) == 0; } while (lived > 0) sleep 0; // until all dies for (i = philos'first; i <= philos'last; i++) { destroy_shared_object(ref forks[(uint)i]); } } 


أخيرًا - جدول ملخص بالامتثال للمتطلبات الوظيفية.
بالتأكيد فاتني شيء أو أسيء تفسيره - صحح ذلك.

مصادر من المادة على جيثب .

Source: https://habr.com/ru/post/ar442372/


All Articles