|
11.3.7 Prozeßbezeichnung und Attribute
Manchmal ist es günstig, auf einen Prozeß zu verweisen, ohne daß
er von einem bestimmten Prozeßtyp sein muß. Ein Server-Prozeß
kann z. B. die letzten Aufrufer aufzeichnen wollen, so daß sie bei nachfolgenden
Aufrufen als "bekannt" identifiziert werden.
Die Objekte des privaten Typs Task_Id im Paket Task_Identification können
Prozesse von jedem Prozeßtyp bezeichnen. Dies macht es möglich nur
ein Unterprogramm zu schreiben, das Prozesse mit unterschiedlichem Prozeßtyp
behandelt.
Das Paket Task_Identifikation beschreibt Operationen und Attribute, die für
die Beschaffung der Bezeichnung eines Prozesses verwendet werden können.
Es gibt das folgende sprachdefinierte Bibliothekspaket:
package Ada.Task_Identification is
type Task_ID is private;
-- Ein Wert des Typs Task_ID bezeichnet einen existierenden
Prozeß.
Null_Task_ID : constant Task_ID;
-- Die Konstante Null_Task_ID definiert keinen Prozeß.
Jedes Objekt
-- des Typs Task_ID wird standardmäßig
mit dem Wert von
-- Null_Task_ID initialisiert.
function "=" ( Left, Right : Task_ID )
return Boolean;
-- Die Funktion "=" gibt nur "wahr"
zurück, wenn die rechte und linke
-- Seite den gleichen Prozeß bezeichnen, oder
beide Null_Task_ID
-- als Wert haben.
function Image ( T : Task_ID ) return String;
-- Die Funktion Image definiert eine implementierungsabhängige
-- Zeichenkette, die den Prozeß T bezeichnet.
Wenn T gleich
-- Null_Task_ID ist, gibt Image eine leere
Zeichenkette zurück.
function Current_Task return Task_ID;
-- Die Funktion Current_Task gibt einen Wert zurück,
der den
-- aufrufenden Prozeß bezeichnet.
-- Current_Task kann in Verbindung mit anderen Operationen,
die
-- einen Prozeß als Argument benötigen,
wie Set_Priority (siehe
-- Anlage D), verwendet werden.
-- Die Funktionen Current_Task kann Task_ID Wert
zurückgeben, der
-- einen Umgebungsprozeß bezeichnet.
procedure Abort_Task ( T: in out Task_ID );
-- Die Wirkung von Abort_Task ist gleich wie bei
der
-- Abbruchanweisung für den durch T bezeichneten
Prozeß. Zusätzlich
-- wird die ganze Partition abgebrochen (siehe Anlage
E), wenn
-- T den Umgebungsprozeß bezeichnet. Abort_Task
ist eine
-- möglicherweise blockierende Operation.
-- Program_Error wird gesetzt, wenn der Wert Null_Task_ID
als
-- Parameter übergeben wird.
function Is_Terminated ( T : Task_ID ) return Boolean;
-- Die Funktionen Is_Terminated geben den Wert des
entsprechenden
-- Prozeßattributes des durch T bezeichneten
Prozesses zurück.
-- Program_Error wird gesetzt, wenn der Wert Null_Task_ID
als
-- Parameter übergeben wird.
function Is_Callable ( T: Task_ID ) return Boolean;
-- Die Funktionen Is_Terminated und Is_Callable
geben den Wert des
-- entsprechenden Prozeßattributes des durch
T bezeichneten
-- Prozesses zurück.
-- Program_Error wird gesetzt, wenn der Wert Null_Task_ID
als
-- Parameter übergeben wird.
private
--... nicht definiert
end Ada.Task_Identification; |
Für das Präfix T, das von einem Prozeßtyp ist (nach einer etwaigen
impliziten Dereferenzierung), wird folgendes Attribut definiert:
TIdentity ergibt einen Wert vom Typ Task_ID, der einen Prozeß bezeichnet.
Für ein Präfix E, das eine Eingangsvereinbarung bezeichnet, wird
folgendes Attribut definiert:
ECaller ergibt einen Wert vom Typ "Task_ID", der den Prozeß
bezeichnet, dessen Aufruf gerade bedient wird. Die Verwendung dieses Attributes
ist nur innerhalb eines Eingangsrumpfes oder einer Annahmeanweisung, die der
mit
E bezeichneten Eingangsanweisung entspricht, erlaubt.
Das Attribut "Caller" kann einen Wert des Typs "Task_ID"
zurückgeben, der den Umgebungsprozeß bezeichnet.
Fehlersituationen
Die Funktion Current_Task darf nicht aus einem Eingangsrumpf oder einem Unterbrechungsbearbeiter
aufgerufen werden. Die Ausnahme "Program_Error" wird ausgelöst
oder es wird ein implementierungsabhängiger Wert vom Typ "Task_ID" zurückgegeben.
Wenn ein Wert von "Task_ID" als Parameter an irgendeine der in diesem
Paket (oder einem Kindpaket) vereinbarten Operationen übergeben wird, und
das entsprechende
Prozeßobjekt nicht mehr existiert, ist die Ausführung des Programms
fehlerhaft.
Beispiel einer einfachen Betriebsmittelzuteilung
Es wird ein einfaches Betriebsmittel betrachtet, das zugeteilt und freigegeben
werden kann. Ohne die Verwendung von Prozeßbezeichnern (task identifier)
ist es schwierig
sicherzustellen, daß ein Betriebsmittel von dem Prozeß freigegeben
wird, der es belegt hat. Der folgende
Programmcode stellt sicher, daß der Prozeß, der das Betriebsmittel
belegt hat,
derjenige ist, der es zurückgibt. Dazu merkt sich das geschützte Objekt "Controller" den
Prozeßbezeichner,
sobald die Belegung mittels "Allocate" erfolgt. Bei der Freigabe mittels "Free"
wird der aktuelle Prozeßbezeichner mit dem gemerkten Prozeßbezeichner
verglichen.
with Ada.Task_Identification; use Ada.Task_Identification;
package Secure_Resource_Allocation is
protected Controller is
entry Allocate;
procedure
Free;
private
Allocated
: Boolean := False;
Current_Owner
: Task_ID := Null_Task_ID;
end Controller;
Not_Allocated : exception;
-- wird von Free angestoßen
end Secure_Resource_Allocation;
package body Secure_Resource_Allocation is
protected body Controller is
entry Allocate when not Allocated
is
begin
Allocated
:= True;
Current_Owner
:= AllocateCaller;
end Allocate;
procedure Free is
begin
if Current_Task
/= Current_Owner then
raise
Not_Allocated;
else
Allocated
:= False;
Current_Owner
:= Null_Task_ID;
end if;
end Free;
end Controller;
end Secure_Resource_Allocation;
|
Das Paket Task_Attributes ordnet einem Prozeß nutzerdefinierte Informationen
zu.
Das generische Bibliothekspaket ermöglicht einem Prozeß, nutzerdefinierte
Attribute zuzuordnen.
with Ada.Task_Identification; use Ada.Task_Identification
generic
type Attribute is private;
Initial_Value : in Attribute;
package Ada.Task_Attribute is
type Attribute_Handle is access all Attribute;
function Value ( T : Task_ID := Current_Task ) return
Attribute;
-- Die Operation Value gibt den Wert des zugehörigen
Attributes von T zurück.
function Reference ( T : Task_ID := Current_Task
) return Attribute_Handle;
-- Die Operation Reference gibt den Zugrifffswert
zurück, der das
-- entsprechende Attribut von T bezeichnet.
procedure Set_Value ( Val : in Attribute; T : in
Task_ID := Current_Task );
-- Die Operation Set_Value führt alle Abschlußarbeiten
auf dem
-- alten Wert des Attributes von T aus und weist
Val dem Attribut zu.
procedure Reinitialize ( T: in Task_ID := Current_Task
);
-- Die Wirkung der Operation Reinitialize ist die
gleiche wie von
-- Set_Value, wobei der Parameter Val durch Initial_Value
ersetzt wird.
end Ada.Task_Attributes; |
Fehlersituationen
- Bei allen in diesem Paket vereinbarten Operationen tritt ein Prozeßfehler
auf, wenn der mit T bezeichnete Prozeß beendet ist. Die Ausnahme "Program_Error"
wird ausgelöst, wenn der Wert von T gleich Null_Task_ID ist.
- Der von einem Aufruf von "Reference" zurückgegebenen Zeigerwert
darf nach einem weiteren Aufruf von "Reinitialize" oder nachdem der
zugehörige Prozeß beendet ist, nicht für das gleiche Prozeßattribut
verwendet werden.
- Wenn ein Wert von "Task_ID" an irgendeine der in diesem Paket vereinbarten
Operationen übergeben wurde, und das entsprechende Prozeßobjekt nicht
mehr existiert, ist die Ausführung des Programms fehlerhaft.
Bemerkungen
- Ein Attribut existiert (nach der Initialisierung) immer und hat einen Anfangswert.
Es braucht bis zur ersten Operation, die das Objekt wahrscheinlich verändert,
keinen Speicher zu belegen. Das gleiche gilt für "Reinitialize".
- Das Ergebnis der Funktion "Reference" sollte mit Vorsicht verwendet
werden; es ist immer sicher
das Ergebnis in dem Prozeßrumpf zu verwenden, auf dessen Attribut zugegriffen
wird. Wenn das Ergebnis jedoch von einem anderen Prozeß verwendet wird,
muß der Programmierer sicher sein, daß der Prozeß auf dessen
Attribut zugegriffen wird, nicht beendet ist. Andernfalls könnte die Programmausführung
fehlerhaft werden.
- Wenn der Parameter T (in einem Aufruf eines Unterprogramms für eine Instanz
dieses Paketes) einen nicht existierenden Prozeß bezeichnet, dann ist die
Ausführung des Programms fehlerhaft.
Beispiel für Prozeßattribute - Periodische Planung
Um die Verwendung der Prozeßattribute zu veranschaulichen, wird die periodische
Planung von Prozessen betrachtet. Normalerweise wird ein periodischer Prozeß
in Ada folgendermaßen strukturiert:
task Cyclic;
task body Cyclic is
Next_Period : Ada.Real_Time.Time := Ada.Real_Time.Clock;
Period : Time_Span := To_Time.Span ( 10.0 ); --
angenommen
begin
loop
-- Anweisungen, die in jeder Periode ausgeführt
werden
Next_Period := Next_Period + Period;
delay until Next_Period;
end loop;
end Cyclic; |
In einigen Anwendungen (z.B. hoch integrierten Systemen) ist es notwendig,
die Einzelheiten der Einplanung vom Prozeß selbst zu trennen. In diesen
Systemen möchte der Prozeß nur einen Aufruf Wait_Until_Next_Schedule
ausführen. Das folgende Paket verwendet Prozeßattribute und Prozeßbezeichner,
um eine Menge von periodischen Prozessen einzuplanen:
with Ada.Task_Identification; use Ada.Task_Identification;
with Ada.Real_Time; use Ada.Real_Time;
package Periodic_Scheduler is
procedure Set_Characteristic (T : Task_ID;
Period
: Time_Span;
First_Schedule
: Time );
procedure Wait_Until_Next_Schedule;
end Periodic_Scheduler;
with Ada.Task.Attributes;
package body Periodic_Scheduler is
Start_Up_Time : Time := Clock;
type Task_Information is record
Period : Time_Span := Time_Span_Zero;
Next_Schedule_Time : Time := Time_Of
( 100000, Time_Span_Zero );
end record;
Default: Task_Information;
-- ein Standardobjekt muß für die folgende
Paketinstantiierung zur Verfügung stehen
package Periodic_Attributes is new Ada.Task_Attributes
( Task_Information, Default );
use Periodic_Attributes;
procedure Set_Characteristic ( T : Task_ID; Period
: Time_Span ; First_Schedule : Time ) is
begin
Set_Value ( ( Period, First_Schedule
), T );
end;
procedure Wait_Until_Next_Schedule is
Task_Info : Task_Information :=
Value;
Next_Time : Time;
begin
Next_Time := Task_Info.Period
+ Task_Info.Next_Schedule_Time;
Set_Value (( Task_Info.Period,
Next_Time ));
delay until Next_Time;
end Wait_Until_Next_Schedule;
end Periodic_Scheduler; |
Periodische Prozesse können jetzt folgendermaßen kodiert werden
und andere Komponenten der Anwendung können die Periode belegen:
task Cyclic;
task body Cyclic is
begin
loop
-- Anweisungen, die in jeder Periode
ausgeführt werden
Periodic_Scheduler.Wait_Until_Next_Schedule;
end loop;
end Cyclic; |
|