IPC-Semaphore löschen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

IPC-Semaphore löschen

Beitrag von Columbus » 09.05.2006 16:14:35

Hallo zusammen,
ich habe hier mal wieder eine Frage die NICHT Debian-Spezifisch ist, aber da ich hier immer gute, <schleim> wenn nicht sogar die beste </schleim> Hilfe bekommen habe versuche ich es mal.

Allllsoooo:
ich möchte eine Semaphore löschen, die von mehreren Prozessen genutzt wird. Prozess A erzeugt eine Semaphore, schreibt die Semaphren-ID in eine Datei und macht sich dann an die Arbeit. Prozess B und C öffnen die Datei lesen die Semaphor-Kennung und greifen auf die Semaphore zu. Das klappt alles wunderbar.

Das Problem ist, wenn Prozess A mit seiner Arbeit fertig ist, löscht er die Semaphore wobei aber Prozess B und C immer noch versuchen darauf zuzugreifen.

Ich habe dazu folgende Löschfunktion geschrieben:

Code: Alles auswählen

/* Loeschen einer Semaphore                                               */
int loesche_semaphore ( int kennung )
{
  union semun mysem;
  int semnum      = 0,
      rueckgabe_1 = 0,
      rueckgabe_2 = 0;

  while ( 1 )
  {
     if ( ( rueckgabe_1 = semctl ( kennung, semnum, GETZCNT, mysem ) ) == -1 )
     {
         perror ( "Fehler beim bestimmen der Semaphor-Wartelist\n" );
         exit ( errno );
     }
     else if ( ( rueckgabe_2 = semctl ( kennung, semnum, GETNCNT, mysem ) ) == -1 )
     {
         perror ( "Fehler beim bestimmen der Semaphor-Wartelist\n" );
         exit ( errno );
     }
     else
     {
         printf ( "Die Menge der Prozesse ist gleich: %d und %d \n", rueckgabe_1, rueckgabe_2 );
         break;
     }
  }

  if ( ( semctl ( kennung, 0, IPC_RMID, mysem ) ) == -1 )
  {
      perror ( "Fehler beim loeschen der Semaphore\n" );
      exit ( errno );
  }

 return ( 1 );
}
Mit dem Flag GETNCNT bzw GETZCNT kann man laut Doku abfragen, wieviele Prozesse noch auf die Semaphore zugreifen. Mit meiner oben gezeigten Funktion wird aber immer nur 0 Prozesse angezeigt. Sowohl mit GETNCNT als auch mit GETZCNT.
Also wie kann ich feststellen, daß ein oder mehrere Prozesse auf die Semaphore zugreifen?

Ich hoffe ich habe meine Problematik einigermaßen Verständlich wiedergegeben :wink:
... und Ihr könnt mir wieder mal helfen.

Vielen Dank im vorraus

Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 09.05.2006 20:50:10

Mit den GETNCNT und GETZCNT kannst du Feststellen ob Prozeße auf gewisse Ereignisse warten. Bei GETZCNT ob Prozesse auf den 0 Wert der Semaphore warten und bei GETNCNT ob Prozesse auf eine Erhöhung des Werts warten.
Columbus hat geschrieben: Mit meiner oben gezeigten Funktion wird aber immer nur 0 Prozesse angezeigt. Sowohl mit GETNCNT als auch mit GETZCNT.
Daher warten deine Prozesse nicht auf diese Ereignisse. Du verwendest die Semaphore wahrscheinlich für "Mutual Exclusion" und nicht für "Producer Consumer Operationen"
Columbus hat geschrieben: Also wie kann ich feststellen, daß ein oder mehrere Prozesse auf die Semaphore zugreifen?
z.B. über eine Semaphore die von jedem Prozeß am Anfang um eins erhöht wird und am Ende um eins vermindert, aber beschreibe einmal wozu du die Semaphore genau verwendest

Gruß
gms

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 09.05.2006 21:50:12

Hallo gms,
aber beschreibe einmal wozu du die Semaphore genau verwendest
OK der Prozess ist folgender:

Code: Alles auswählen

int main ( int argc, char *argv[] )
{
  int schlafzeit  = 0,
      laufvar_a   = 0,
      laufvar_b   = 0,
      server_flag = 0,
      sem_id      = 0;
  char zeichen;

  if ( argc < 3 )
  {
    printf ( "Usage: gibaus char int \n");
    exit ( 0 );
  }
  else if ( argc == 4 )
  {
    server_flag = atoi ( argv[3] );
  }
  
  schlafzeit = atoi ( argv[2] );
  zeichen    = *argv[1];

  printf ( "Die Zahl ist: %i, das Zeichen: %c\n", schlafzeit, zeichen );

// Server-Prozess erzeugt die Semaphoren und schreibt die Semaphoren-ID in die Datei "myfile"
// Client-Prozesse lesen aus dieser Datei die Semaphoren-ID 
  if ( server_flag )
  {
    printf ( "Ich bin der Server!\n" );
    sem_id = erzeuge_semaphore( server_flag );
    printf ( "schreibe in Datei %i ", sem_id );
    erzeuge_datei ( "myfile", sem_id );
  }
  else
  {
    sem_id = erhalte_semaphoren_id ( "myfile" );
    printf ( "Ich bin der Client, ich habe die Sem_Id: %i bekommen\n", sem_id );
  }

  for ( laufvar_a = 0; laufvar_a <= ITERATIONS; laufvar_a++ )
  {
     p ( sem_id );
     for ( laufvar_b = 0; laufvar_b <= ANZ_ZEICHEN; laufvar_b++ )
     {
      // putchar ( zeichen );
         printf ( "%c",zeichen );
     }
     putchar ( '\n' );
     v ( sem_id );
     sleep ( schlafzeit );
  }

  if ( server_flag )
  {
     loesche_semaphore ( sem_id );
     loesche_datei ( "myfile" );
  }
 exit( 0 );
}
Es werden mehrere Prozesse von dem Programm s.o. gestartet. Zwei werden mit einer Zahl und einem Charakter eines mit einer zusätzlichen Zahl gestartet. Dieser Prozess ist der Serverprozess ( er erhält den Serverflag ). Dieser Prozess erzeugt die Semaphoren, schreibt die Kennung in eine Datei und fängt dann an mit der Ausgabe seiner 10 Zeichen.
Bevor er die Ausgibt muß er allerdings die p-Operation durchführen. Wenn er fertig ist führt er die v-Operation aus. Die beiden anderen Prozesse lesen aus der Datei die Semaphoren-ID und können auch p-und v-Operationen auf dieser Semaphore ausführen. Auf dieses Art gibt jeder der drei Prozesse abwechselnd zehn mal zehn Zeichen aus. Das Problem ist, der erste Prozess, der Serverprozess löscht die Semaphoren wenn er fertig ist! Die beiden anderen Prozesse haben das Nachsehen.

Wie kann man das verhindern, daß der Serverprozess erst dann die Semaphoren löscht, wenn kein Prozess mehr auf diese zugreift?
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 09.05.2006 22:54:04

statt der Datei könntest du die Funktion "ftok" verwenden um einen IPC Schlüssel zu erhalten. Das folgende Beispiel findest du wenn du unter "man semget" nach "ftok" suchst:
man semget hat geschrieben: /* Get unique key for semaphore. */
if ((semkey = ftok("/tmp", 'a')) == (key_t) -1) {
perror("IPC error: ftok"); exit(1);
}

/* Get semaphore ID associated with this key. */
if ((semid = semget(semkey, 0, 0)) == -1) {
Columbus hat geschrieben: Wie kann man das verhindern, daß der Serverprozess erst dann die Semaphoren löscht, wenn kein Prozess mehr auf diese zugreift?
über einen Prozeßzähler
Wenn du schon Semaphoren verwendest, würde sich eine zusätzliche Semaphore dafür anbieten. Über "semget" erstellt du ja das Semaphoren Set, dort kannst du eine zusätzliche Semaphore ordern. Jeder Clientprozeß erhöht diese Semaphore um eins sobald er gestartet wird (dort kannst du SEM_UNDO verwenden). Der Serverprozeß wartet am Ende bis die Semaphore auf 0 steht und löscht nachher das Semaphor Set


Gruß
gms

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 10.05.2006 16:53:38

Die Funktion ftok ist mir nicht ganz klar. Irgend etwas muß ja ausgetauscht werden um verschiedene nicht verwandet Prozesse dazu zu bringen auf die gleiche Semaphore zuzugreifen. Das muß ich mir noch mal genauer ansehen!

Aber die Lösung mit einer zweiten Zählsemaphore ist genial und einfach 8)

Vielen Dank

Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 10.05.2006 20:55:13

Columbus hat geschrieben:Die Funktion ftok ist mir nicht ganz klar. Irgend etwas muß ja ausgetauscht werden um verschiedene nicht verwandet Prozesse dazu zu bringen auf die gleiche Semaphore zuzugreifen.
Ja aber das kann auch statisch "ausgetauscht" (eigentlich vereinbart) werden,
Du definierst dein "Protokoll" so, daß alle Programme die auf diese Semaphore zugreifen wollen, zuerst eine Key über ftok ordern müssen und zwar über einen speziellen Pfad und ein spezielles Projektzeichen.

Die Hauptfunktion der Prozesse könnte z.B. so ausschauen:

Code: Alles auswählen

int worker(char workID, int server) {
  key_t semkey;
  int semID;

  mkdir(KEYPATH, 0);
  if ( (semkey = ftok(KEYPATH, KEYPROJ)) == -1 ) fatalerror("get ipc key");
  if ( (semID = semget(semkey, 2, IPC_CREAT | S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP)) == -1) fatalerror("get sem id");  
  if ( server && semop(semID, &semunlock, 1) == -1 ) fatalerror("unlock sem"); 
  if ( !server && semop(semID, &semproccounterinc, 1) == -1 ) fatalerror("increment process counter");
  if ( !server ) sleep(1);
  if ( semop(semID, &semlock, 1) == -1 ) fatalerror("lock sem");

  printf("in worker %c\n", workID);

  if ( semop(semID, &semunlock, 1) == -1 ) fatalerror("unlock sem");

  if (server) {
    if ( semop(semID, &semproccounterwait, 1) == -1 ) fatalerror("wait ");
    if ( semctl(semID, 0, IPC_RMID) == -1 ) fatalerror("remove sem");
  }
  return 0; 
}
Damit wir sehen ob der Serverprozeß brav auf die Beendigung der Clients wartet, habe ich die Clients mit einem "sleep" verzögert.

Hier ist der komplette Code zum Testen:
http://nopaste.debianforum.de/3090

Gruß
gms

Antworten