1. परिचय — Ada और real-time का गहरा रिश्ता
पिछले लेख “Ada में सुरक्षित concurrency” में हमने Ada के task और protected object से safe concurrent programming की basics देखी थीं। इस बार उसी लाइन पर आगे बढ़ते हैं — एक और tightly constrained domain में: real-time systems।
Real-time system में “correctness” का मतलब सिर्फ़ logically सही result नहीं है। वह result deadline के अंदर मिलना भी correctness का हिस्सा है। एक millisecond देर से आया सही जवाब, गलत जवाब जितना ही खतरनाक होता है।
Ada इस requirement के लिए language specification के Annex D (Real-Time Systems) के रूप में एक पूरा real-time feature set देती है। यह “बाद में library से जोड़ दिया गया” नहीं है — language runtime में ही बनी real-time guarantee है।
Ada के real-time features (Annex D):
- Task priority और preemption (FIFO_Within_Priorities)
- Ceiling_Locking protocol (priority inversion रोकना)
- delay until से absolute time पर periodic execution
- Ravenscar profile (safety-critical subset)
- Timing events (बिना polling के time-driven wakeup)
- Execution time monitoring (Ada.Execution_Time)
- Multi-periodic scheduling
इस लेख में हम इन्हें 8 practical code examples से step-by-step देखेंगे। हर snippet conceptually अकेला चल सकता है, लेकिन 04/05 में कई compilation units हैं — उन्हें gnatchop से split करके फिर gnatmake करें।
Audience और prerequisites: पिछले लेख वाले task, rendezvous, और protected object की basics पता होनी चाहिए। Embedded devices या high-integrity control software में दिलचस्पी रखने वाले developers के लिए है; Ada syntax की शुरुआत यहाँ नहीं है।
Verification environment: लेख के 8 examples GNAT 13.3.0 (Ubuntu 24.04, x86-64) पर build होते confirm किए गए हैं। अध्याय 3 और 5 में छपे run outputs भी उसी environment से हैं। Priority और preemption असल में कैसे behave करते हैं, यह OS और GNAT runtime पर depend करता है (अध्याय 12), इसलिए output की बारीकियाँ environment के हिसाब से बदल सकती हैं।
लेख में आए code fragments chapter-wise files में बाँटकर GitHub पर reference collection के रूप में उपलब्ध हैं।
ada-real-time-systems - komurasoft-blog-samples (GitHub)
इस लेख का knowledge map
Ada की language specification का Annex D task priority पर आधारित FIFO_Within_Priorities, protected object को automatically ceiling priority देने वाला Ceiling_Locking protocol, cumulative drift रोकने वाला delay until से periodic execution, static analysis के लिए आसान Ravenscar profile, बिना polling वाले timing events, और per-task execution time measurement तक language runtime में ही embed करता है। Ceiling_Locking उस priority inversion को रोकता है जो 1997 के Mars Pathfinder पर असल में हुई और जिसने spacecraft को बार-बार reset कराया: protected object enter करने वाले task को automatically ceiling priority पर उठा देता है। Ravenscar profile relative delay और select statement वगैरह मना करके Ada के task को static timing analysis के लिए आसान subset तक सीमित करता है, और GNAT में gnat.adc file में pragma लिखकर enable होता है। लेकिन priority का असल mapping OS और GNAT runtime पर depend करता है।
flowchart LR
accTitle: Ada real-time systems programming का knowledge map
accDescr: Ada Annex D task और protected object के ऊपर priority-based dispatch, Ceiling_Locking, delay until, Ravenscar profile, timing events और execution time measurement कैसे embed करता है, तथा Ceiling_Locking Mars Pathfinder जैसी priority inversion कैसे रोकता है — दिखाने वाला diagram
ada_annex_d["Annex D (Ada का real-time systems standard)"]
ceiling_locking["Ceiling_Locking protocol"]
fifo_within_priorities["FIFO_Within_Priorities"]
ada_delay_until["delay until (absolute time पर delay)"]
ravenscar_profile["Ravenscar profile"]
ada_timing_events["timing events (Ada.Real_Time.Timing_Events)"]
ada_execution_time["Ada.Execution_Time (execution time measurement)"]
ada_task["Ada task (concurrency)"]
protected_object["protected object"]
gnat["GNAT"]
priority_inversion["priority inversion"]
mars_pathfinder_priority_inversion["Mars Pathfinder का priority inversion incident"]
ada_relative_delay["relative delay (delay statement)"]
cumulative_drift["periodic execution का cumulative drift"]
ada_annex_d -->|"use करता है"| fifo_within_priorities
ada_annex_d -->|"use करता है"| ceiling_locking
ada_annex_d -->|"use करता है"| ada_delay_until
ada_annex_d -->|"use करता है"| ravenscar_profile
ada_annex_d -->|"use करता है"| ada_timing_events
ada_annex_d -->|"use करता है"| ada_execution_time
ada_annex_d -->|"require करता है"| ada_task
ada_annex_d -.->|"require करता है"| protected_object
ada_annex_d -.->|"require करता है"| gnat
ceiling_locking -->|"prevent करता है"| priority_inversion
priority_inversion -->|"cause हो सकता"| mars_pathfinder_priority_inversion
ravenscar_profile -->|"require करता है"| ada_task
ravenscar_profile -->|"require करता है"| fifo_within_priorities
ravenscar_profile -->|"require करता है"| ceiling_locking
ravenscar_profile -.->|"से configure"| gnat
ada_relative_delay -->|"cause हो सकता"| cumulative_drift
ada_delay_until -->|"prevent करता है"| cumulative_drift
ada_relative_delay -->|"recommended नहीं"| ravenscar_profile
ada_timing_events -->|"use करता है"| ceiling_locking
ada_timing_events -->|"use करता है"| protected_object
ada_execution_time -->|"require करता है"| ada_task
Diagram में solid line हमेशा लागू रहने वाला relation दिखाती है और dashed line conditional relation दिखाती है (शर्तें detail page पर हर relation के explanation में दी गई हैं)। Relations की पूरी list (कुल 21, evidence और certainty सहित) तथा मुख्य concepts की definitions knowledge map की detail page पर संकलित हैं (जापानी में)। Data: JSON-LD / Turtle
2. Real-time system क्या है
पहले terminology साफ़ कर लें।
| Concept | Explanation |
|---|---|
| Hard real-time | Deadline miss का मतलब system का catastrophic failure है (flight control, airbag, pacemaker) |
| Soft real-time | Deadline miss undesirable है, लेकिन कभी-कभार miss चल सकता है (video streaming, games) |
| Deadline | Absolute time जिस तक task पूरा होना चाहिए |
| Period | वह interval जिस पर task बार-बार release होता है |
| WCET (Worst-Case Execution Time) | Task का worst-case execution time |
| Jitter | Periodic execution की timing variation |
| Schedulability | “क्या यह task set सभी deadlines पूरा करते हुए चल सकता है” — यह गुण। इसे theoretically confirm करना schedulability analysis है (response-time analysis वगैरह) |
| Ravenscar profile | Ada के tasking को statically analyzable subset तक सीमित करने वाला convention। नाम UK के गाँव Ravenscar से है, जहाँ drafting meeting हुई थी (अध्याय 6) |
Real-time design में हर task के लिए WCET <= deadline एक ज़रूरी necessary condition है। अकेले इससे पूरे system की deadline guarantee नहीं मिलती। Blocking time, priority assignment, jitter, interrupts, और runtime/OS behaviour समेत response-time analysis अलग से चाहिए। Practice में margin रखने के लिए WCET < deadline लक्ष्य रखते हैं। Ada के real-time features उसी analysis को आसान बनाने वाला predictable execution model language level पर देते हैं।
flowchart LR
accTitle: Real-time requirements और schedulability
accDescr: Hard और soft real-time की deadline, period, WCET, jitter Ada Annex D के FIFO_Within_Priorities, Ceiling_Locking, delay until के साथ schedulability analysis में कैसे जुड़ते हैं।
HRT[Hard real-time] -->|"deadline miss = catastrophic failure"| Examples[Flight control<br/>Airbag<br/>Pacemaker]
SRT[Soft real-time] -->|occasional miss tolerated| Examples2[Video streaming<br/>Games<br/>UI]
Ada[Ada Annex D<br/>Predictability के mechanisms] --> Mechanism[FIFO_Within_Priorities<br/>Ceiling_Locking<br/>delay until]
subgraph Requirements[Real-time requirements]
D[Deadline<br/>Complete करने का absolute time]
P[Period<br/>Repeat interval]
W[WCET<br/>Worst-case execution time]
J[Jitter<br/>Period की variation]
end
D --> Analysis[Schedulability analysis]
P --> Analysis
W --> Analysis
J --> Analysis
HRT --> Analysis
SRT --> Analysis
Mechanism --> Analysis
Analysis --> Constraint["Necessary condition: WCET <= deadline<br/>Sufficiency response-time analysis से confirm"]
Real-time systems में सबसे खतरनाक phenomena में से एक priority inversion है। यही problem 1997 में Mars Pathfinder पर असल में आई, और spacecraft बार-बार reset होता रहा।
sequenceDiagram
accTitle: Priority inversion कैसे बनता है
accDescr: Low-priority task lock hold किए हुए, medium-priority task उसे preempt करे, और high-priority task अनिश्चित काल तक block रह जाए।
participant S as Scheduler
participant L as Low-priority task
participant H as High-priority task
participant M as Medium-priority task
participant R as Shared resource
L->>R: Lock acquire
activate L
Note over L: Critical section चल रही है
Note over S,L: H wake होता है, scheduler L को suspend करता है
deactivate L
activate H
H->>R: Lock acquire की कोशिश
Note over H: Lock wait पर block! (L अभी hold किए हुए है)
deactivate H
Note over S,L: H lock wait पर है, इसलिए L फिर resume
activate L
Note over L: Lock release की तरफ जारी...
Note over S,L: M wake होता है, scheduler L को suspend करता है
deactivate L
activate M
Note over L: L lock release नहीं कर सकता
Note over M: M चलता रहता है (H और L दोनों stuck)
Note over H: [priority inversion] High-priority अनिश्चित काल block
deactivate M
Low-priority task lock hold किए हुए medium-priority task से preempt हो जाता है, और high-priority task अनिश्चित काल तक block रह जाता है। Mars Pathfinder पर असल mitigation VxWorks का priority inheritance enable करना था। Ada उसी class की problem के लिए अलग mechanism देती है — language feature Ceiling_Locking।
3. Task priority की basics — FIFO_Within_Priorities
FIFO_Within_Priorities Ada Annex D में माँग सकने वाली standard priority-based dispatching policy है। Policy explicitly न लिखें तो default implementation-defined है; GNAT कई targets पर इसी परिवार की policy इस्तेमाल करता है। एक ही priority के अंदर FIFO (first-in, first-out) चलता है, और ऊँची priority वाला task निचली priority वाले को preempt (बीच में काट) करता है।
-- 01_task_priority.ada
-- Task priority और FIFO_Within_Priorities की basic form
-- Configuration pragma context clause से पहले रखें
pragma Task_Dispatching_Policy (FIFO_Within_Priorities);
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
procedure Task_Priority_Demo is
task High_Priority_Task is
pragma Priority (Priority'Last);
pragma Storage_Size (4 * 1024);
end High_Priority_Task;
task Low_Priority_Task is
pragma Priority (Priority'First);
pragma Storage_Size (4 * 1024);
end Low_Priority_Task;
task body High_Priority_Task is
begin
Put_Line ("[T=0.0s] High priority task started");
delay until Clock + Milliseconds (100);
Put_Line ("[T=0.1s] High priority task completed");
end High_Priority_Task;
task body Low_Priority_Task is
begin
Put_Line ("[T=0.0s] Low priority task started");
delay until Clock + Milliseconds (500);
Put_Line ("[T=0.5s] Low priority task completed");
end Low_Priority_Task;
begin
Put_Line ("=== Task Priority Demo (FIFO_Within_Priorities) ===");
Put_Line ("Main: waiting for tasks to complete...");
delay until Clock + Milliseconds (800);
Put_Line ("Main: done");
end Task_Priority_Demo;
मुख्य बातें:
pragma Priorityहर task को static priority देता है।Priority'Lastसबसे ऊँची,Priority'Firstसबसे निचली।- इस demo के task bodies भारी computation नहीं करते;
delay untilसे दिए समय तक wait करते हैं। यहाँ देखना यह है कि दोनों एक साथ runnable हों तो high-priority task को पहले execution मिलती है। - यह diagram
FIFO_Within_Prioritiesका अलग-अलग priorities के बीच preemption वाला हिस्सा दिखाता है। एक ही priority के अंदर FIFO order देखने के लिए बराबर priority वाले कई tasks का अलग example चाहिए। - असल systems में
System.Default_Priorityको आधार मानकर relative priorities design करना आम है।
sequenceDiagram
accTitle: FIFO_Within_Priorities पर high-priority preemption
accDescr: दोनों tasks runnable हों तो scheduler सबसे ऊँची priority चुनता है, delay until के बाद completion order 100ms फिर 500ms रहता है।
participant S as Scheduler
participant Main as Main task
participant HP as High-priority task<br/>(Priority=Last)
participant LP as Low-priority task<br/>(Priority=First)
Main->>HP: Task create
Main->>LP: Task create
Note over HP,LP: T=0ms: दोनों tasks runnable
S->>HP: सबसे ऊँची priority HP चुनता है
activate HP
Note over HP: Start log print करता है
HP->>S: delay until T+100ms पर block
deactivate HP
S->>LP: फिर LP execute करता है
activate LP
Note over LP: Start log print करता है
LP->>S: delay until T+500ms पर block
deactivate LP
Note over S: T=100ms: HP wake
S->>HP: HP execute करता है
activate HP
Note over HP: Completion log print करता है
deactivate HP
Note over S: T=500ms: LP wake
S->>LP: LP execute करता है
activate LP
Note over LP: Completion log print करता है
deactivate LP
Note over Main: (T=800ms) Main ends
Run example (GNAT 13.3.0 / Ubuntu 24.04, x86-64):
$ gnatchop -w 01_task_priority.ada . # → task_priority_demo.adb
$ gnatmake task_priority_demo.adb
$ ./task_priority_demo
[T=0.0s] High priority task started
[T=0.0s] Low priority task started
=== Task Priority Demo (FIFO_Within_Priorities) ===
Main: waiting for tasks to complete...
[T=0.1s] High priority task completed
[T=0.5s] Low priority task completed
Main: done
ध्यान दें: दोनों tasks के start logs main के heading से पहले आ रहे हैं। Declarative part के tasks enclosing subprogram के body में enter होने से पहले activate होते हैं, इसलिए यह क्रम बन सकता है। और ये दो start-log lines हर run पर आपस में पलट भी सकती हैं। अध्याय 12 के मुताबिक pragma Priority असल scheduling पर कैसे map होता है, यह OS और GNAT runtime पर है; generic Linux पर priority के हिसाब से startup order guarantee नहीं है। इस example में स्थिर रूप से दिखने वाली बात delay until से तय 0.1s / 0.5s की completion order है।
Ada की priority range (GNAT default):
Priority'First = 0 (सबसे निचली)
Priority'Last = 30 (सबसे ऊँची; OS पर depend)
4. Ceiling_Locking — language खुद priority inversion रोकती है
Real-time systems की सबसे tricky problems में priority inversion है। High-priority task उस lock की wait करता है जो low-priority task hold किए हुए है, और वह low-priority task medium-priority task से preempt हो जाता है — नतीजा, high-priority task अनिश्चित काल तक block।
Ada इस problem के लिए Ceiling_Locking protocol सीधे protected objects में बैठाती है।
-- 02_ceiling_locking.ada
-- Ceiling_Locking protocol से priority inversion रोकना
-- Configuration pragma context clause से पहले रखें
pragma Locking_Policy (Ceiling_Locking);
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
procedure Ceiling_Locking_Demo is
Ceiling : constant System.Any_Priority := System.Any_Priority'Last;
protected Shared_Data is
pragma Priority (Ceiling);
procedure Write (V : Integer);
function Read return Integer;
private
Value : Integer := 0;
end Shared_Data;
protected body Shared_Data is
procedure Write (V : Integer) is
begin
Value := V;
end Write;
function Read return Integer is
begin
return Value;
end Read;
end Shared_Data;
task Producer is
pragma Priority (Priority'Last);
pragma Storage_Size (4 * 1024);
end Producer;
task Consumer is
pragma Priority (Priority'First);
pragma Storage_Size (4 * 1024);
end Consumer;
task body Producer is
begin
Put_Line ("[T=0.0s] Producer (high prio): about to write");
Shared_Data.Write (42);
Put_Line ("[T=0.0s] Producer (high prio): write done");
delay until Clock + Milliseconds (100);
end Producer;
task body Consumer is
begin
delay until Clock + Milliseconds (10);
Put_Line ("[T=0.01s] Consumer (low prio): about to read");
declare
V : Integer;
begin
V := Shared_Data.Read;
Put_Line ("[T=0.01s] Consumer (low prio): read done, got" &
Integer'Image (V));
end;
delay until Clock + Milliseconds (100);
end Consumer;
begin
Put_Line ("=== Ceiling_Locking Demo ===");
Put_Line ("Main: producer priority = Last, consumer priority = First");
Put_Line ("Ceiling = Any_Priority'Last, locking = Ceiling_Locking");
delay until Clock + Milliseconds (300);
Put_Line ("Main: done");
end Ceiling_Locking_Demo;
Ceiling_Locking कैसे काम करता है:
- Protected object पर
pragma Priority (Ceiling)से ceiling priority सेट करें। - कोई भी task protected object enter करे, enter करते ही automatically ceiling priority पर उठ जाता है।
- इसलिए जो task protected object इस्तेमाल कर रहा है, उसे medium-priority task preempt नहीं कर सकता।
- Protected object से निकलते ही मूल priority वापस आ जाती है।
नीचे वाला diagram ठीक पहले वाले sample का strict time trace नहीं है। यह अवधारणा दिखाता है कि दूसरे diagram वाला priority inversion pattern Ceiling_Locking से कैसे रुकता है।
sequenceDiagram
accTitle: Ceiling_Locking priority inversion कैसे रोकता है
accDescr: Protected object enter करते ही low-priority task ceiling 30 पर उठ जाता है, इसलिए medium-priority preempt नहीं कर पाता; high-priority ceiling check पास करके lock free होने का wait करता है।
participant S as Scheduler
participant L as Low-priority task<br/>(priority=10)
participant M as Medium-priority task<br/>(priority=20)
participant H as High-priority task<br/>(priority=30)
participant PO as Protected object<br/>(ceiling priority=30)
Note over PO: Active priority > ceiling वाले caller पर Program_Error<br/>Diagram का H(30) ceiling(30) के बराबर है, इसलिए enter कर सकता है
L->>PO: Protected operation enter करता है
activate L
Note over L,PO: Execution priority 30 पर उठती है
Note over S: M wake होता है
Note over S,L: L ceiling priority 30 पर चल रहा है<br/>M(20) preempt नहीं कर सकता
Note over S: H wake होता है
Note over S,H: H(30) ceiling check पास करता है<br/>लेकिन L PO इस्तेमाल कर रहा है, इसलिए wait
L->>PO: Operation चलाता है
L->>PO: Protected operation से निकलता है
deactivate L
Note over L: Priority 10 पर लौटती है
Note over S,H: PO free होने के बाद H execute होता है
activate H
H->>PO: Protected operation enter करता है
Note over H,PO: H(30) = ceiling(30), contention resolve होने के बाद enter कर सकता है
H->>PO: Protected operation से निकलता है
deactivate H
Design rule: Protected object की ceiling priority उस object इस्तेमाल करने वाले सभी tasks की सबसे ऊँची priority से कम नहीं होनी चाहिए। इसे तोड़कर, ceiling से ऊँची active priority वाला task protected operation call करे तो Ada
Program_Errorउठाकर design mistake detect कर सकती है।
C में pthread mutex से यही करने के लिए PTHREAD_PRIO_PROTECT attribute अलग से सेट करना पड़ता है। Ada में यह language का standard feature है।
5. delay until — periodic task बिना drift के
Real-time systems का basic pattern periodic task है। Fixed interval पर बार-बार चलने वाले task में cumulative timing error (drift) रोकना बहुत ज़रूरी है।
Ada का delay until इसी problem को साफ़ तरीके से हल करता है।
-- 03_periodic_task.ada
-- delay until से periodic task — cumulative drift रोकें
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
procedure Periodic_Task_Demo is
Period_MS : constant Time_Span := Milliseconds (200);
Cycles : constant Positive := 5;
task Sensor_Reader is
pragma Priority (Priority'Last - 2);
pragma Storage_Size (4 * 1024);
end Sensor_Reader;
task body Sensor_Reader is
Start_Time : constant Time := Clock;
Next_Release : Time := Start_Time + Period_MS;
Cycle_Count : Natural := 0;
begin
Put_Line ("[Sensor] Periodic task starts, period=" &
To_Duration (Period_MS)'Image & "s, cycles=" &
Natural'Image (Cycles));
for I in 1 .. Cycles loop
delay until Next_Release;
Cycle_Count := Cycle_Count + 1;
Put_Line ("[Sensor] Cycle" & Natural'Image (Cycle_Count) &
" at" & Duration'Image (To_Duration (Clock - Start_Time)) & "s");
Next_Release := Next_Release + Period_MS;
end loop;
Put_Line ("[Sensor] Periodic task finished. Actual elapsed:" &
Duration'Image (To_Duration (Clock - Start_Time)) & "s");
end Sensor_Reader;
begin
Put_Line ("=== Periodic Task Demo (delay until) ===");
Put_Line ("Main: waiting for" & Natural'Image (Cycles) & " cycles...");
delay until Clock + Milliseconds (1500);
Put_Line ("Main: done");
end Periodic_Task_Demo;
delay until क्यों:
| Approach | Problem |
|---|---|
delay Period; |
हर iteration का processing time जुड़ता जाता है, period धीरे-धीरे खिसकती है (cumulative drift) |
delay until Next_Release; Next_Release := Next_Release + Period; |
Absolute time पर टिका है; एक iteration देर से चले तो भी अगला release time सही रहता है |
हाँ, delay until यह automatically guarantee नहीं करता कि processing period के अंदर समाएगी। अगर काम अगले wake time को पार कर चुका, तो वह delay until लगभग तुरंत लौट आता है — system को इसे deadline miss मानकर overload की तरह treat करना चाहिए।
delay के साथ:
T=0ms → work(15ms) → delay 100ms → T=115ms → work(10ms) → ...
Actual interval: 115ms, 110ms, ... (processing time जमा होता है)
delay until के साथ:
Next_Release: 100ms, 200ms, 300ms, ... (absolute time)
T=0ms → work(15ms) → delay until 100ms → T=100ms → work(10ms) → delay until 200ms
Actual interval: 100ms, 100ms, ... (processing time से interval नहीं बदलता)
Run example (GNAT 13.3.0 / Ubuntu 24.04, x86-64):
$ gnatchop -w 03_periodic_task.ada . # → periodic_task_demo.adb
$ gnatmake periodic_task_demo.adb
$ ./periodic_task_demo
[Sensor] Periodic task starts, period= 0.200000000s, cycles= 5
=== Periodic Task Demo (delay until) ===
Main: waiting for 5 cycles...
[Sensor] Cycle 1 at 0.200326876s
[Sensor] Cycle 2 at 0.400159550s
[Sensor] Cycle 3 at 0.600183089s
[Sensor] Cycle 4 at 0.800327451s
[Sensor] Cycle 5 at 1.000260472s
[Sensor] Periodic task finished. Actual elapsed: 1.000297359s
Main: done
हर cycle पर करीब 0.2–0.3 ms का wakeup delay दिखता है, लेकिन वह delay अगली period पर नहीं जाता। पाँचवीं cycle पर भी start time से फ़र्क 1 ms से कम है। अगर delay Period; लिखा होता, तो यही देरी हर बार जुड़कर पाँच cycles बाद नज़र आने वाला फ़र्क बन जाती। ये संख्याएँ generic Linux पर एक sample हैं; hard real-time environment की guarantee नहीं।
आगे के हर periodic task में यही delay until pattern इस्तेमाल होगा।
flowchart TB
accTitle: delay versus delay until और deadline miss
accDescr: Relative delay cumulative drift लाता है, delay until absolute time पर टिका रहता है, और overrun पर delay until तुरंत लौट आता है।
subgraph Bad["delay Period - cumulative drift"]
B1[T=0ms: compute 15ms] --> B2[delay 100ms → 115ms पर wake]
B2 --> B3[compute 10ms → 125ms]
B3 --> B4[delay 100ms → 225ms पर wake]
B4 --> B5["Actual interval: 115ms, 110ms..."]
end
subgraph Good["delay until - absolute time"]
G1[Next = T+100ms] --> G2[compute 15ms]
G2 --> G3[delay until T+100ms → 100ms पर wake]
G3 --> G4[compute 10ms]
G4 --> G5[Next = T+200ms → 200ms पर wake]
G5 --> G6["Actual interval: 100ms, 100ms..."]
end
subgraph Overrun["Period overrun - deadline miss"]
O1[Next = T+100ms] --> O2[compute 130ms]
O2 --> O3[delay until T+100ms तुरंत लौटता है]
O3 --> O4[Delay detect करके overload की तरह treat करें]
end
Bad --> Drift[समय के साथ error जमा होता है]
Good --> Stable[Cumulative drift रुकता है]
Good --> Overrun
6. Ravenscar profile — verifiable real-time subset
Ada का tasking शक्तिशाली है, लेकिन safety-critical systems में “बहुत शक्तिशाली” खुद problem बन जाता है। Dynamic task creation, select statement, abort statement — ये WCET की static analysis मुश्किल कर देते हैं।
Ravenscar profile Ada का जवाब है। Tasking को statically analyzable, deterministic subset तक सीमित करता है।
-- 04_ravenscar_profile.ada
-- Ravenscar profile की basic form
-- Compile करते समय gnat.adc में pragma Profile (Ravenscar); लिखें
-- Build: gnatchop -w 04_ravenscar_profile.ada .
-- → ravenscar_state.ads / ravenscar_state.adb / ravenscar_demo.adb में split होता है
-- gnat.adc तैयार करके gnatmake ravenscar_demo.adb
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
package Ravenscar_State is
protected Signal is
pragma Priority (System.Default_Priority + 5);
entry Wait_For_Release;
procedure Release;
private
Released : Boolean := False;
end Signal;
task Periodic_Worker is
pragma Priority (System.Default_Priority + 1);
pragma Storage_Size (4 * 1024);
end Periodic_Worker;
task Monitor is
pragma Priority (System.Default_Priority);
pragma Storage_Size (4 * 1024);
end Monitor;
end Ravenscar_State;
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
package body Ravenscar_State is
protected body Signal is
entry Wait_For_Release when Released is
begin
Released := False;
end Wait_For_Release;
procedure Release is
begin
Released := True;
end Release;
end Signal;
task body Periodic_Worker is
Start_Time : constant Time := Clock;
Next_Release : Time := Start_Time + Milliseconds (100);
Period : constant Time_Span := Milliseconds (100);
Cycle_Count : Natural := 0;
begin
Put_Line ("[Worker] Ravenscar periodic task starts");
for I in 1 .. 4 loop
delay until Next_Release;
Cycle_Count := Cycle_Count + 1;
Put_Line ("[Worker] Cycle" & Natural'Image (Cycle_Count) &
" at" & Duration'Image (To_Duration (Clock - Start_Time)) & "s");
Signal.Release;
Next_Release := Next_Release + Period;
end loop;
Put_Line ("[Worker] Finished demo, waiting (Ravenscar: No_Task_Termination)");
loop
delay until Clock + Seconds (1);
end loop;
end Periodic_Worker;
task body Monitor is
begin
Put_Line ("[Monitor] Waiting for signals...");
for I in 1 .. 4 loop
Signal.Wait_For_Release;
Put_Line ("[Monitor] Received signal" & Natural'Image (I));
end loop;
Put_Line ("[Monitor] All signals received, waiting (Ravenscar: No_Task_Termination)");
loop
delay until Clock + Seconds (1);
end loop;
end Monitor;
end Ravenscar_State;
with Ravenscar_State; use Ravenscar_State;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Real_Time; use Ada.Real_Time;
procedure Ravenscar_Demo is
begin
Put_Line ("=== Ravenscar Profile Demo ===");
Put_Line ("(compile with: gnatmake -gnatec=gnat.adc ravenscar_demo)");
Put_Line ("Main: waiting for Ravenscar tasks...");
delay until Clock + Milliseconds (800);
Put_Line ("Main: demo window elapsed; waiting forever (Ravenscar: No_Task_Termination)");
loop
delay until Clock + Seconds (1);
end loop;
end Ravenscar_Demo;
Ravenscar profile की restrictions:
| Forbidden feature | Reason |
|---|---|
Dynamic task creation (new या access types) |
Runtime memory allocation non-deterministic है |
select statement |
सिर्फ़ multiple alternatives नहीं; पूरा select control-flow analysis मुश्किल करता है |
abort statement |
Asynchronous abort से state unpredictable हो जाती है |
Ada.Task_Attributes |
Runtime-dynamic behaviour |
| Dynamic priority change | Scheduling analysis की धारणा runtime पर बदल जाती है |
Relative delay (delay) |
Cumulative drift पैदा हो जाता है, इसलिए absolute time वाला delay until इस्तेमाल करें |
| Protected object पर एक से ज़्यादा entries | Blocking conditions और analysis cases बढ़ते हैं |
| Task termination | Ravenscar सभी tasks को non-terminating मानता है |
requeue statement |
Control-flow tracking जटिल हो जाता है |
इन restrictions से Ravenscar-compliant program static timing analysis के अनुकूल बनता है। यही गुण DO-178C (aviation software) और ISO 26262 (automotive functional safety) जैसे safety standards माँगते हैं। नीचे सूची मुख्य restrictions का excerpt है; असल profile में No_Task_Hierarchy और Detect_Blocking जैसी runtime/analyzability वाली extra rules भी हैं।
flowchart TB
accTitle: Ravenscar restrictions और safety standards
accDescr: Ravenscar Ada tasking को restrictions और required policies से सीमित करता है ताकि static timing analysis आसान हो और DO-178C, ISO 26262, IEC 62304 की तरफ जाए।
Full[Complete Ada tasking] --> Profile[Ravenscar profile]
Profile --> Restrict[Restrictions]
Profile --> Policy[Required policies]
Restrict --> R1[Dynamic task creation मना]
Restrict --> R2[select statement मना]
Restrict --> R3[abort statement मना]
Restrict --> R4[Task_Attributes मना]
Restrict --> R5[Protected object पर 1 entry की सीमा]
Restrict --> R6[requeue statement मना]
Restrict --> R7[Relative delay मना<br/>delay until इस्तेमाल करें]
Restrict --> R8[Dynamic priority change मना]
Restrict --> R9[Task termination मना<br/>सभी tasks non-terminating]
Policy --> P1[FIFO_Within_Priorities]
Policy --> P2[Ceiling_Locking]
Restrict --> Benefit["इससे आसान होता है:<br/>static timing analysis"]
Policy --> Benefit
Benefit --> DO178[DO-178C<br/>Aviation software]
Benefit --> ISO26262[ISO 26262<br/>Automotive functional safety]
Benefit --> IEC62304[IEC 62304<br/>Medical device software]
Ravenscar profile enable करने के लिए gnat.adc में यह लिखें:
pragma Profile (Ravenscar);
7. Timing events — बिना polling के time-driven wakeup
बहुत से real-time systems में बार-बार यही चाहिए: “दिए समय पर high-priority task को wake करो।” Naive implementation timer poll करती है। Ada इससे बेहतर mechanism देती है — timing events।
-- 05_timing_events.ada
-- Timing events (Ada.Real_Time.Timing_Events)
-- High-priority task को बिना polling के wake करने का mechanism
-- Build: gnatchop -w 05_timing_events.ada .
-- → signal_pkg.ads / signal_pkg.adb / timing_events_demo.adb में split होता है
-- gnatmake timing_events_demo.adb
pragma Locking_Policy (Ceiling_Locking);
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
with Ada.Real_Time.Timing_Events; use Ada.Real_Time.Timing_Events;
package Signal_Pkg is
protected type Signal_Type is
pragma Priority (System.Interrupt_Priority'Last);
entry Wait_For_Event;
procedure Fire (Event : in out Timing_Event);
private
Fired : Boolean := False;
end Signal_Type;
S : Signal_Type;
end Signal_Pkg;
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
with Ada.Real_Time.Timing_Events; use Ada.Real_Time.Timing_Events;
package body Signal_Pkg is
protected body Signal_Type is
entry Wait_For_Event when Fired is
begin
Fired := False;
end Wait_For_Event;
procedure Fire (Event : in out Timing_Event) is
begin
Fired := True;
end Fire;
end Signal_Type;
end Signal_Pkg;
with Signal_Pkg; use Signal_Pkg;
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
with Ada.Real_Time.Timing_Events; use Ada.Real_Time.Timing_Events;
procedure Timing_Events_Demo is
pragma Priority (29);
Timer_1 : Timing_Event;
Timer_2 : Timing_Event;
task Reactor is
pragma Priority (System.Default_Priority + 5);
pragma Storage_Size (4 * 1024);
end Reactor;
task body Reactor is
begin
Put_Line ("[Reactor] Waiting for timing events...");
S.Wait_For_Event;
Put_Line ("[Reactor] Got event #1");
S.Wait_For_Event;
Put_Line ("[Reactor] Got event #2");
Put_Line ("[Reactor] Done");
end Reactor;
begin
Put_Line ("=== Timing Events Demo ===");
Put_Line ("Scheduling two timers at +100ms and +250ms...");
Set_Handler (Timer_1, Clock + Milliseconds (100), S.Fire'Access);
Set_Handler (Timer_2, Clock + Milliseconds (250), S.Fire'Access);
delay until Clock + Milliseconds (500);
Put_Line ("Main: done");
end Timing_Events_Demo;
Timing event कैसे चलता है:
1. Set_Handler(Timer_1, T+100ms, S.Fire'Access) — absolute time पर handler register करें
2. T+100ms बीतते ही — runtime S.Fire को **ceiling priority** पर call करता है
3. Fire, Fired flag True करता है — barrier खुल जाता है
4. Reactor task Wait_For_Event से wake होता है
ज़रूरी बात: इस example में Ceiling_Locking explicit है, और Fire handler protected object की procedure है, इसलिए ceiling priority पर चलती है। Timing-event handler के रूप में इस्तेमाल होने वाली protected procedure interrupt-level ceiling priority वाले protected object पर रखें — यहाँ System.Interrupt_Priority'Last। इससे timing event के handling के दौरान priority inversion नहीं बनता।
8. Protected object से real-time queue
Real-time में बार-बार दिखने वाला pattern producer–consumer है। Sensor data बनाता है, control task उसे consume करता है — यहाँ buffer का mutual exclusion और blocking efficiently चाहिए।
Ada के protected object और entry barrier से इसे barrier-based synchronization की तरह लिख सकते हैं। अंदर runtime mutual exclusion संभालता है, इसलिए application code में mutex या condition variable सीधे लिखने की ज़रूरत नहीं।
-- 06_protected_queue.ada
-- Protected object से real-time data sharing
-- Pipeline: Producer -> Bounded_Buffer -> Consumer
-- Compile करते समय gnat.adc में pragma Locking_Policy (Ceiling_Locking); लिखें
pragma Locking_Policy (Ceiling_Locking);
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
procedure Protected_Queue_Demo is
Buffer_Size : constant := 4;
type Buf_Array is array (1 .. Buffer_Size) of Integer;
protected Bounded_Buffer is
pragma Priority (System.Any_Priority'Last);
entry Put (Item : Integer);
entry Get (Item : out Integer);
private
Buf : Buf_Array;
Count : Natural := 0;
Head : Positive := 1;
Tail : Positive := 1;
end Bounded_Buffer;
protected body Bounded_Buffer is
entry Put (Item : Integer) when Count < Buffer_Size is
begin
Buf (Tail) := Item;
Tail := (Tail mod Buffer_Size) + 1;
Count := Count + 1;
end Put;
entry Get (Item : out Integer) when Count > 0 is
begin
Item := Buf (Head);
Head := (Head mod Buffer_Size) + 1;
Count := Count - 1;
end Get;
end Bounded_Buffer;
task Producer is
pragma Priority (System.Default_Priority + 2);
pragma Storage_Size (4 * 1024);
end Producer;
task Consumer is
pragma Priority (System.Default_Priority + 1);
pragma Storage_Size (4 * 1024);
end Consumer;
task body Producer is
Next_Release : Time := Clock + Milliseconds (50);
Period : constant Time_Span := Milliseconds (50);
begin
for I in 1 .. 6 loop
Bounded_Buffer.Put (I);
Put_Line ("[Producer] Put" & Integer'Image (I));
delay until Next_Release;
Next_Release := Next_Release + Period;
end loop;
Put_Line ("[Producer] Done");
end Producer;
task body Consumer is
Item : Integer;
Next_Release : Time := Clock + Milliseconds (80);
Period : constant Time_Span := Milliseconds (80);
begin
delay until Clock + Milliseconds (30);
for I in 1 .. 6 loop
Bounded_Buffer.Get (Item);
Put_Line ("[Consumer] Got" & Integer'Image (Item));
delay until Next_Release;
Next_Release := Next_Release + Period;
end loop;
Put_Line ("[Consumer] Done");
end Consumer;
begin
Put_Line ("=== Protected Queue Demo (Ceiling_Locking) ===");
Put_Line ("Buffer size = 4; Producer every 50ms, Consumer every 80ms");
delay until Clock + Milliseconds (800);
Put_Line ("Main: done");
end Protected_Queue_Demo;
Design की मुख्य बातें:
entry Put when Count < Buffer_Size— buffer full हो तो Producer automatically block होता है।entry Get when Count > 0— buffer empty हो तो Consumer automatically block होता है।pragma Priority (System.Any_Priority'Last)— ceiling locking से Producer और Consumer के बीच priority inversion नहीं बनता।- Barrier condition protected object की internal state (
Count) से defined है, और lock release पर automatically फिर evaluate होती है।
इस code में application-level mutex, semaphore, या condition variable नहीं है। ज़रूरी wait protected object के entry barrier से लिखा गया है।
stateDiagram-v2
accTitle: Bounded buffer empty, partial, full
accDescr: Put और Get barrier Count से empty, partial, full states के बीच ले जाते हैं; empty पर Get और full पर Put block होते हैं।
Empty: खाली / Count=0
Partial: कुछ items / Count=1..Buffer_Size-1
Full: Full / Count=Buffer_Size
[*] --> Empty: Initial state
Empty --> Partial: Put (1 item जोड़ा)
Partial --> Partial: Put / Get
Partial --> Empty: Get (आखिरी item निकाला)
Partial --> Full: Put (आखिरी खाली slot भरा)
Full --> Partial: Get (एक slot खाली)
Empty --> Empty: Get block (barrier Count=0)
Full --> Full: Put block (barrier Count=Buffer_Size)
Put सफल होने पर waiting Get, और Get सफल होने पर waiting Put की barrier फिर evaluate होती है। Diagram जिस state पर हो, protected operation complete होते ही यही होता है।
9. Execution time मापना — runtime monitoring का पहला कदम
Real-time system की schedulability आंकने के लिए हर task का execution time (CPU time) सही जानना ज़रूरी है। Ada का Ada.Execution_Time package per-task CPU consumption देता है।
-- 07_execution_time.ada
-- Execution time control (Execution_Time)
-- हर task का CPU time measure करना
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
with Ada.Execution_Time;
use type Ada.Execution_Time.CPU_Time;
procedure Execution_Time_Demo is
package ET renames Ada.Execution_Time;
task Busy_Worker is
pragma Priority (System.Default_Priority + 1);
pragma Storage_Size (4 * 1024);
end Busy_Worker;
task body Busy_Worker is
Wall_Start : Time;
Cpu_Start : ET.CPU_Time;
Dummy : Integer := 0;
pragma Volatile (Dummy);
begin
Wall_Start := Clock;
Cpu_Start := ET.Clock;
Put_Line ("[Worker] Starting compute-bound work...");
for I in 1 .. 20_000_000 loop
Dummy := Dummy + 1;
end loop;
Put_Line ("[Worker] Dummy =" & Integer'Image (Dummy));
declare
Wall_Elapsed : constant Duration :=
To_Duration (Clock - Wall_Start);
Cpu_Span : constant Time_Span :=
ET.Clock - Cpu_Start;
begin
Put_Line ("[Worker] Done, wall time:" &
Duration'Image (Wall_Elapsed) & "s");
Put_Line ("[Worker] CPU time consumed:" &
Duration'Image (To_Duration (Cpu_Span)) & "s");
end;
end Busy_Worker;
Cpu_Start_Main : constant ET.CPU_Time := ET.Clock;
begin
Put_Line ("=== Execution Time Demo ===");
delay until Clock + Milliseconds (500);
declare
Cpu_Span : constant Time_Span := ET.Clock - Cpu_Start_Main;
begin
Put_Line ("Main: CPU time consumed after 500ms:" &
Duration'Image (To_Duration (Cpu_Span)) & "s");
end;
Put_Line ("Main: done");
end Execution_Time_Demo;
Wall-clock time बनाम CPU time:
Wall-clock time (Wall Clock): Ada.Real_Time.Clock
→ असल elapsed time। Blocked या preempted रहने का समय भी शामिल है।
CPU time (Execution Time): Ada.Execution_Time.Clock
→ सिर्फ़ वह समय जब task CPU पर वास्तव में चल रहा था।
→ Blocked / preempted time count नहीं होता।
यह फ़र्क execution-time monitoring और WCET validation की शुरुआत है। Busy_Worker जब delay until पर wait करता है तो CPU time नहीं बढ़ता — सिर्फ़ असल computation के दौरान बढ़ता है। Main task के delay until Clock + Milliseconds(500) के दौरान भी CPU time लगभग शून्य होना चाहिए। हाँ, CPU time का measurement true WCET guarantee नहीं करता। Cache, pipeline, memory contention समेत WCET के लिए अलग से static analysis या target पर validation चाहिए।
flowchart LR
accTitle: Wall-clock time बनाम CPU time
accDescr: Wall clock में wait, block, preemption शामिल हैं; CPU time सिर्फ़ असल computation गिनता है, measurement true WCET नहीं है।
subgraph Wall[Wall-clock time]
W1["Elapsed total: 500ms"] --> W2["Breakdown: compute + wait + blocked + preempted"]
end
subgraph CPU[CPU time]
C1["CPU total: 120ms"] --> C2["Breakdown: actual computation only"]
end
Wall --> Diff[फ़र्क = wait / block / preemption time]
CPU --> Diff
Diff --> Insight[CPU time असल compute cost देखता है<br/>WCET validation और monitoring में सहायक<br/>Wait, block, preemption time निकाल देता है]
Insight --> Caveat[सावधानी<br/>Measurement true WCET guarantee नहीं करता<br/>Static analysis या target validation जरूरी है]
10. Integration demo — multi-periodic real-time system
अब तक के सारे टुकड़े — priority, Ceiling_Locking, delay until, protected object — जोड़कर एक typical multi-periodic real-time system बनाते हैं।
-- 08_multiperiodic.ada
-- Multi-periodic real-time system का integration demo
-- Fast period (100ms) वाला sensor-read task
-- Slow period (400ms) वाला control task
-- Ceiling_Locking से data sharing
pragma Locking_Policy (Ceiling_Locking);
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
with Ada.Real_Time; use Ada.Real_Time;
procedure Multiperiodic_Demo is
package Int_IO is new Ada.Text_IO.Integer_IO (Integer);
protected Shared_Sensor is
pragma Priority (System.Any_Priority'Last);
procedure Write (V : Integer);
function Read return Integer;
private
Value : Integer := 0;
end Shared_Sensor;
protected body Shared_Sensor is
procedure Write (V : Integer) is
begin
Value := V;
end Write;
function Read return Integer is
begin
return Value;
end Read;
end Shared_Sensor;
task Fast_Sensor is
pragma Priority (System.Default_Priority + 3);
pragma Storage_Size (4 * 1024);
end Fast_Sensor;
task body Fast_Sensor is
Next_Release : Time := Clock + Milliseconds (100);
Period : constant Time_Span := Milliseconds (100);
Cycle : Natural := 0;
begin
Put_Line ("[Fast] Sensor reader starts (100ms period)");
for I in 1 .. 12 loop
delay until Next_Release;
Cycle := Cycle + 1;
Shared_Sensor.Write (Cycle * 10);
Next_Release := Next_Release + Period;
end loop;
Put_Line ("[Fast] Done");
end Fast_Sensor;
task Slow_Controller is
pragma Priority (System.Default_Priority + 2);
pragma Storage_Size (4 * 1024);
end Slow_Controller;
task body Slow_Controller is
Next_Release : Time := Clock + Milliseconds (150);
Period : constant Time_Span := Milliseconds (400);
Cycle : Natural := 0;
Raw : Integer;
begin
Put_Line ("[Slow] Controller starts (400ms period)");
for I in 1 .. 3 loop
delay until Next_Release;
Cycle := Cycle + 1;
Raw := Shared_Sensor.Read;
Put_Line ("[Slow] Cycle" & Natural'Image (Cycle) &
" reads sensor =" & Integer'Image (Raw));
Next_Release := Next_Release + Period;
end loop;
Put_Line ("[Slow] Done");
end Slow_Controller;
begin
Put_Line ("=== Multiperiodic Real-Time System Demo ===");
Put_Line ("Fast sensor (100ms) x 12 + Slow controller (400ms) x 3");
Put_Line ("Ceiling_Locking prevents priority inversion on shared data");
delay until Clock + Milliseconds (2000);
Put_Line ("Main: done");
end Multiperiodic_Demo;
System की बनावट:
नीचे वाला diagram sample code के release times (fast sensor 100ms period, slow control 150ms offset पर 400ms period) पर, समझाने के लिए execution times मानकर बना schedule है। Code में 80ms का control computation नहीं है, इसलिए यह measured trace नहीं। Fast sensor की priority ऊँची हो तो slow control के चलते हुए fast sensor का release आने पर slow control बीच में रुकता है। Diagram में पढ़ने की आसानी के लिए हर slow-control cycle पर एक representative preemption दिखाई है; असल run में हर 100ms boundary पर fast sensor release होता है।
flowchart TB
accTitle: Fast sensor और slow control का illustrative schedule
accDescr: 100ms fast sensor ऊँची priority से 400ms slow control को preempt करता है; हर slow cycle पर एक representative preemption दिखाई है।
Assumption["Example assumption<br/>Fast sensor: 10ms work<br/>Slow control: 80ms work"]
subgraph Cycle1["Slow control cycle 1 (release=150ms)"]
direction LR
C1F1["100-110ms<br/>Fast sensor #1"] --> C1S1["150-200ms<br/>Slow control #1 part A"]
C1S1 --> C1F2["200-210ms<br/>Fast sensor #2<br/>P+3 इसलिए preempt"]
C1F2 --> C1S2["210-240ms<br/>Slow control #1 part B"]
end
subgraph Cycle2["Slow control cycle 2 (release=550ms)"]
direction LR
C2S1["550-600ms<br/>Slow control #2 part A"] --> C2F6["600-610ms<br/>Fast sensor #6<br/>P+3 इसलिए preempt"]
C2F6 --> C2S2["610-640ms<br/>Slow control #2 part B"]
end
subgraph Cycle3["Slow control cycle 3 (release=950ms)"]
direction LR
C3S1["950-1000ms<br/>Slow control #3 part A"] --> C3F10["1000-1010ms<br/>Fast sensor #10<br/>P+3 इसलिए preempt"]
C3F10 --> C3S2["1010-1040ms<br/>Slow control #3 part B"]
end
Assumption --> C1F1
C1S2 --> C2S1
C2S2 --> C3S1
यह pattern industrial control और robot control में आम “fast sensor acquisition + slow control loop” की typical संरचना है।
11. जहाँ Ada के real-time features काम आते हैं
Ada के real-time features इन क्षेत्रों में खास मूल्य देते हैं।
flowchart TB
accTitle: Annex D जिन domains में काम आता है
accDescr: Ada Annex D aerospace, railway, automotive, medical devices, industrial control और defense में safety standards के साथ जुड़ता है।
Ada[Ada Annex D<br/>Real-time features] --> Aero[Aerospace<br/>DO-178C]
Ada --> Rail[Railway<br/>EN 50128 family]
Ada --> Auto[Automotive<br/>ISO 26262]
Ada --> Medical[Medical devices<br/>IEC 62304]
Ada --> Industrial[Industrial control<br/>IEC 61508 family]
Ada --> Defense[Defense / high-integrity systems]
Aero --> A1[Flight control<br/>Proven application area]
Aero --> A2[Satellite / spacecraft control]
Rail --> R1[Signalling systems]
Rail --> R2[Automatic train control]
Auto --> Au1[Safety-related ECU candidate]
Auto --> Au2[C / MISRA-C वाले mainstream में<br/>limited, selective use]
Medical --> M1[Pacemaker]
Medical --> M2[Infusion pump]
Industrial --> I1[Robot control]
Industrial --> I2[NC machine tools]
Defense --> D1[Mission computers]
Defense --> D2[Long-life operational systems]
Diagram में सिर्फ़ automotive पर “limited, selective use” लिखा है, क्योंकि वजह भाषा की fitment से ज़्यादा existing ecosystem का आकार है। Vehicle software में AUTOSAR जैसे industry-standard API, supplier से आने वाला code, certified compiler और verification tools, और engineers की आबादी तक — सब C और MISRA-C को आधार मानकर जमा हुआ है। भाषा बदलना, भले एक component की बात हो, उसके आसपास के tools, procurement और verification process को एक साथ फिर से खड़ा करने के बराबर है। इसलिए Ada पूरे vehicle का default बनने से ज़्यादा, खास तौर पर ऊँची guarantee चाहिए वाले कुछ components पर, या जहाँ Ada की assets और development setup पहले से है, वहाँ selective रूप से इस्तेमाल होती है। इसका उलटा यह है कि aerospace और railway जैसे क्षेत्रों में, जहाँ ecosystem पहले से high-integrity तरफ झुका है, यह रुकावट शुरू से नहीं होती।
12. सावधानियाँ और सीमाएँ
Ada के real-time features शक्तिशाली हैं, लेकिन हर problem का हल नहीं।
1. Platform dependence:
pragma Priorityका असल mapping execution environment (OS + GNAT runtime) पर depend करता है। Linux पर यहSCHED_FIFOपर map होता है, लेकिन Windows पर पूरी preemption guarantee नहीं हो सकती।
2. Ravenscar की सीमाएँ:
- Dynamic task creation मना है, इसलिए startup पर सभी tasks statically declare करने पड़ते हैं। इससे design की आज़ादी सीमित होती है।
3. WCET measurement की सीमा:
Ada.Execution_Timemeasurement है, guarantee नहीं। Cache miss और pipeline hazard समेत true WCET static analysis tools से अलग verify करना पड़ता है।
4. Overhead:
- Protected object की barrier evaluation entry complete/cancel होने पर, और protected object से निकलते समय automatically चलती है। बार-बार call होने वाले protected objects पर इस overhead का हिसाब रखना चाहिए।
5. Toolchain barrier:
- Ada के real-time features पूरा इस्तेमाल करने के लिए सही cross-compiler और runtime चाहिए। खासकर embedded targets पर vendor-supplied runtime पर depend रहना पड़ता है।
13. सार
इस लेख में Ada Annex D के real-time features 8 code examples से step-by-step देखे।
| Feature | क्या देता है |
|---|---|
| Task priority | Preemptive, priority-based scheduling |
| Ceiling_Locking | Language में बसी priority inversion prevention |
delay until |
Cumulative drift रोके periodic execution |
| Ravenscar profile | Static analysis के अनुकूल tasking subset |
| Timing events | बिना polling के time-driven wakeup |
| Protected queue | Protected object की barrier-based synchronization |
| Execution time measurement | Per-task CPU time monitoring |
| Multi-periodic integration | अलग-अलग periods वाले tasks को साथ चलाने का design |
Ada के real-time features का सार यही है कि वे बाद में जोड़े गए नहीं हैं। Priority inversion दबाने वाले locking नियम, periodic execution के लिए absolute time, execution-time monitoring — ये language specification का हिस्सा हैं। Deadline हासिल करना अभी भी design और analysis से confirm होता है, लेकिन उसके लिए ज़रूरी आधार language runtime दे देता है — यही बड़ी ताकत है।
अगला कदम: Ada में real-time development खुद आज़माने के लिए Alire से GNAT toolchain install करें, और इस लेख का sample code gnatchop + gnatmake से build करें।
Ada concurrency की basics (task, rendezvous, protected object) पिछले लेख “Ada में सुरक्षित concurrency” में हैं।
14. संदर्भ
- Ada Reference Manual - Annex D: Real-Time Systems
- Ravenscar Profile Definition (ISO/IEC TR 24718:2005)
- GNAT Real-Time Topics (AdaCore)
- The Ravenscar Profile for High-Integrity Systems (AdaCore)
- Rate Monotonic Analysis (Liu & Layland, 1973)
- Alire - Ada Package Manager
- Ada sample code collection (GitHub)
संबंधित लेख
निकटवर्ती विषयों में गहराई से जाने के लिए समान टैग वाले नवीनतम लेख।
Ada में सुरक्षित concurrency — task और protected object की practical guide
Ada के language-embedded concurrency — task और protected object — का introductory लेख। rendezvous (entry/accept), selective accept, prote...
Ada में generic programming — type पर contract लिखकर zero-cost reuse
Ada की generic programming को generic subprogram, generic package, formal subprogram, type category और practical design guidelines तक sys...
Windows Virtualization Internals (भाग 3) — सेकंडों में boot होने वाली VMs: WSL2, Windows Sandbox और containers इतने हल्के क्यों हैं
WSL2 और Windows Sandbox सेकंडों में start होकर इतने हल्के क्यों लगते हैं? यह लेख dynamic base image और direct map से dynamic memory alloc...
Windows Virtualization Internals (भाग 2) — वो मेमोरी जिसे कर्नेल भी नहीं देख सकता: VBS, HVCI और Credential Guard कैसे काम करते हैं
Compatible hardware पर clean install में VBS default से enable होता है और hypervisor तथा SLAT से kernel से मज़बूत isolation बनाता है। यह ...
Windows virtualization की गहराई (भाग 1) — आपका Windows वास्तव में कहाँ चल रहा है? Hypervisor और partitions
जब आप Hyper-V enable करते हैं, तो host Windows खुद root partition के रूप में hypervisor के ऊपर चलता है। यह लेख VT-x, SLAT और VMBus की भूम...
संबंधित विषय
ये पृष्ठ विषय को सेवाओं और निर्णयों के व्यापक संदर्भ में रखते हैं।
Windows के तकनीकी विषय
Windows विकास, बग जाँच और मौजूदा संपत्तियों के उपयोग का प्रवेश-द्वार।
अक्सर पूछे जाने वाले प्रश्न
इस लेख के विषय पर परामर्श में अक्सर पूछे जाने वाले प्रश्न।
- Ada का Annex D क्या है?
- Ada language specification का हिस्सा, real-time systems के लिए standard किया गया feature set है। इसमें FIFO_Within_Priorities से priority-based preemptive scheduling, priority inversion रोकने वाला Ceiling_Locking protocol, delay until से absolute time पर periodic execution, Ravenscar profile, timing events, और Ada.Execution_Time से per-task execution time measurement शामिल हैं। खास बात यह है कि ये किसी library से बाद में जोड़े नहीं गए — language runtime में ही बने हैं।
- Priority inversion क्या है? Ada इसे कैसे रोकती है?
- Low-priority task lock hold किए हुए रहता है, उसे medium-priority task preempt कर देता है, और उसी lock की wait कर रहा high-priority task अनिश्चित काल तक block रह जाता है — यही priority inversion है। 1997 में Mars Pathfinder पर यही हुआ और spacecraft बार-बार reset होता रहा। Ada में Ceiling_Locking protocol language feature है: protected object enter करते ही task automatically ceiling priority पर उठ जाता है, इसलिए medium-priority task उसे preempt नहीं कर पाता।
- Ravenscar profile क्या है?
- Safety-critical systems के लिए Ada के tasking को statically analyzable, deterministic subset तक सीमित करने वाला profile है। Dynamic task creation, select statement, abort statement, relative delay, requeue statement वगैरह मना हैं। इन restrictions से static timing analysis आसान होता है, और DO-178C (aviation software) या ISO 26262 (automotive functional safety) जैसे safety standards की properties पूरा करना आसान पड़ता है। GNAT में gnat.adc में pragma Profile (Ravenscar) लिखकर इसे enable करते हैं।
- Periodic task में delay की जगह delay until क्यों?
- Relative delay में हर iteration का processing time जुड़ता जाता है, इसलिए period धीरे-धीरे खिसकती है — cumulative drift। delay until अगला release time absolute time से तय करता है, इसलिए एक बार processing देर से चली तो भी बाद के release times सही रहते हैं। हाँ, अगर processing अगले wake time को पार कर जाए तो delay until लगभग तुरंत लौट आता है; उसे deadline miss मानकर overload की तरह handle करने का अलग design चाहिए।