Shared Memory und Pointer Arrays

Moin zusammen.

Ich bearbeite gerade eine Aufgabe und benötige Unterstützung.

Es soll ein Programm erstellt werden welches mit 3 Argumenten (Zahlen) gestartet wird. Das Programm erzeugt einen Shared Memory Bereich und legt die 3 Werte darin ab. Erzeugt daraufhin einen Kind-Prozess welcher diese Werte aus dem Shared Memory ausliest und Berechnungen anstellt.

Bis dato sieht meine Lösung so aus:

#include 
#include 
#include 
#include 
#include 

main(int argc, char \*argv[]){
 printf("char=%i\n",sizeof(char));
 printf("int=%i\n\n",sizeof(int));

 int shmsize=3\*(sizeof(char));


 printf("Programmbegin. PID: %d\n",getpid());
 int shmid1=shmget(1110,shmsize,IPC\_CREAT|0666);
 printf("Die SHMID lautet: %i\n",shmid1);


 char \*shm[3];
 shm[0]=shmat(shmid1,NULL,0);
 shm[1]=shm[0]+sizeof(char)+1;
 shm[2]=shm[1]+sizeof(char)+1;

 printf("Pointer in SHM[0]: %p\n",shm[0]);
 printf("Pointer in SHM[1]: %p\n",shm[1]);
 printf("Pointer in SHM[2]: %p\n",shm[2]);

 strcpy(shm[0],argv[1]);
 strcpy(shm[1],argv[2]);
 strcpy(shm[2],argv[3]);

 printf("SHM[0] verweist auf Inhalt: %s\n",shm[0]);
 printf("SHM[1] verweist auf Inhalt: %s\n",shm[1]);
 printf("SHM[2] verweist auf Inhalt: %s\n",shm[2]);

 if(!fork()){
 printf("Kind erzeugt: %d\n",getpid());
 //Hier erfolgt der Zugriff des Kind-Prozesses auf 
 //Shared Memory und Berechnung
 }
printf("Ende %d\n",getpid());
}

Die Ausgabe:

ubuntu@ubuntu:~/Desktop$ gcc pm11.c -o pm11.o
ubuntu@ubuntu:~/Desktop$ ./pm11.o 3 4 5
char=1
int=4

Programmbegin. PID: 10480
Die SHMID lautet: 655369
Pointer in SHM[0]: 0xb78a8000
Pointer in SHM[1]: 0xb78a8002
Pointer in SHM[2]: 0xb78a8004
SHM[0] verweist auf Inhalt: 3
SHM[1] verweist auf Inhalt: 4
SHM[2] verweist auf Inhalt: 5
Ende 10480
Kind erzeugt: 10481
Ende 10481

Ich störe mich aber an Folgendem:

  1. Da berchnet werden soll, sollte ich mit Integer und nicht mit Character arbeiten. Wie stelle ich es an: einfach char durch int ersetzen?

  2. Der shmget() Aufruf scheitert wenn ich statt shmsize einen Wert eingebe: z.B. 6

  3. Warum darf ich in

    printf(„SHM[0] verweist auf Inhalt: %s\n“,shm[0]);

nicht shm statt shm[0] eingeben?

4)Angenommen es ist erwünscht mit char zu arbeiten. Wäre mein Programm dann i.O. oder gäbe es etwas zu verbessern?

Bin für jeden Hinweis dankbar.
Vitali

Für die Fragen 1 und 2 habe ich etwas wie eine Lösung gefunden.

Zu 2) Unter Knoppix 6.2 konnte ich den Code kompilieren und erfolgreich ausführen. Also ist es eine Compiler-Eigenheit gewesen?

Zu 1) Hier habe ich tatsächlich fast nur die char durch int ersetzt und erfolgreich kompiliert und ausgeführt. Ebenfalls unter Knoppix.
Hier der neue Code:

#include 
#include 
#include 
#include 
#include 

main(int argc, char \*argv[]){
 printf("char=%i\n",sizeof(char));
 printf("int=%i\n\n",sizeof(int));

**int a=atof(argv[1]);  
 int b=atof(argv[2]);  
 int c=atof(argv[3]);**  

 int shmsize=3\*(sizeof( **int** ));
 printf("%i\n",shmsize);


 printf("Programmbegin. PID: %d\n",getpid());
 int shmid1=shmget(1110,shmsize,IPC\_CREAT|0666);
 printf("Die SHMID lautet: %i\n",shmid1);

**int** \*shm[3];
 shm[0]=shmat(shmid1,NULL,0);
 shm[1]=shm[0]+sizeof( **int** );
 shm[2]=shm[1]+sizeof( **int** );

**\*shm[0]=a; // Statt strcpy, da keine Strings mehr,...  
 \*shm[1]=b; // ...sondern int verwendet werden  
 \*shm[2]=c;**  
 printf("SHM[0] verweist auf Inhalt: %i\n",\*shm[0]);
 printf("SHM[1] verweist auf Inhalt: %i\n",\*shm[1]);
 printf("SHM[2] verweist auf Inhalt: %i\n",\*shm[2]);

 if(!fork()){
 printf("Kind erzeugt: %d\n",getpid());
 //Hier erfolgt der Zugriff des Kind-Prozesses auf 
 //Shared Memory und Berechnung
 }
printf("Ende %d\n",getpid());
}

Vitali

Hallo Vitali,

ich bin eigentlich eher Java Entwickler, habe aber auch schon einiges mit C programmiert und versuch dir mal ein wenig zu helfen.

Zu deinen Fragen:

  1. Da berchnet werden soll, sollte ich mit Integer und nicht
    mit Character arbeiten. Wie stelle ich es an: einfach char
    durch int ersetzen?

Die Startparameter deines C-Programms werden als Char-Pointer Array übergeben, also nicht als Character sondern als Pointer auf Speicheradressen hinter denen sich Characterfolgen befinden.
Dein Ansatz diese in Integer zu konvertieren ist korrekt. Allerdings konvertiert atof eine Zeichenkette in einen double. Die korrekte hier zu verwendende funktion wäre atoi. Eventuell wäre es noch von Vorteil die erhaltenen Startparameter vor der Konvertierung auf ihren Datentyp zu überprüfen.

  1. Der shmget() Aufruf scheitert wenn ich statt shmsize einen
    Wert eingebe: z.B. 6

Warum dein Compiler hier die Eingabe von Bsp. 6 nicht akzeptiert könntest du dir mittels errno ausgeben lassen. Für shmget gibt es definierte Fehlercodes, die dich auf den Fehler hinweisen. Vermutlich liegt es daran, dass der Wert 6 einfach kleiner dem minimal benötigten Speicherplatz ist.

  1. Warum darf ich in

printf(„SHM[0] verweist auf Inhalt:
%s\n“,shm[0]);

nicht shm statt shm[0] eingeben?

So wie du shm[] deklarierst

char *shm[3];

erhälst du ein Array mit drei Pointern, die jeweils auf eine Zeichenfolge von Charactern verweisen. shm ist ein Array und diese können nicht ausgegeben werden, höchstens der Pointer auf ein Array. %s wird verwendet um Zeichenfolgen (Strings) auszugeben. shm[0] zeigt auf eine Zeichenfolge, daher kann diese mit %s Ausgegeben werden.

4)Angenommen es ist erwünscht mit char zu arbeiten. Wäre mein
Programm dann i.O. oder gäbe es etwas zu verbessern?

Auf der ersten Blick schein dein Programm ansonsten in Ordnung zu sein. Eventuell macht sich die ein oder andere Validierung noch ganz gut.

Ich hoffe ich konnte dir irgendwie wenigstens ein bisschen weiterhelfen.

Gruß Björn

Vorsichtiger programmieren

Dein Ansatz diese in Integer zu konvertieren ist korrekt.
Allerdings konvertiert atof eine Zeichenkette in einen double.
Die korrekte hier zu verwendende funktion wäre atoi. Eventuell
wäre es noch von Vorteil die erhaltenen Startparameter vor der
Konvertierung auf ihren Datentyp zu überprüfen.

Diesen Hinweis würde ich auf das ganze Programm beziehen, denn an vielen Stellen gibt es Typfehler und keine Abprüfungen, ob die Verwendung von Werten überhaupt erlaubt oder der Aufruf von Funktionen überhaupt erfolgreich war.

Deshalb sollte vorab erst mal verifiziert werden, ob die Anzahl der übergebenen Parameter stimmen, bevor man blind z.B. auf argv[2] zugreift.

Die unterschiedlichen Datentypen können zu weiteren Fehlern führen, wenn Speicherplatz für einen Integer geholt werden soll, dann aber ein strcpy auf die Adresse durchgeführt wird.
Gerade für die ersten Programme ist es deshalb sehr ratsam, die Compiler-Warnings streng einzustellen und konsequent jede einzelne Warnung durch saubere Umsetzung zu ersetzen.

Selbst wenn man sich einem sauberen Programm durch Trial-and-Error annähern will, ist es hilfreich auch Zwischenergebnisse abzuprüfen oder auszugeben.

Ciao, Allesquatsch

Moin zusammen.

Björn, danke für die Erklärung. Es hilft weiter.

Nachdem ich das *.c File unter Knoppix, wie geschrieben, kompilieren konnte und die Aufgabe zu Ende brachte, versuchte ich dieses File erneut unter Ubuntu zu kompilieren. Und es war erfolgreich.
Ich hatte zu 100% ausgemacht, dass der Einsatz eines Wertes (12) statt der Variablen shmsize (ergab auch 12) die Kompilierung abbrach. Das einzige was jetzt anders ist: Ubuntu neu gestartet (arbeite mit Ubuntu von CD). Seltsam.

Warum dein Compiler hier die Eingabe von Bsp. 6 nicht akzeptiert könntest du dir mittels errno ausgeben lassen.

Björn, bitte um Erklärung wie mir die errno bei der Kompilierung hilft. Kenne die strerror() und errno nur vom Sehen in fremden Scripten hier im Forum, aber meiner Meinung nach helfen diese nur wenn das Programm läuft. Oder?

Die Startparameter deines C-Programms werden als Char-Pointer Array übergeben.
Dein Ansatz diese in Integer zu konvertieren ist korrekt.

Wollte schlau sein und mein main wie folgt angehen: main (int argc, int *argv[]). Beim auslesen von argv[] aber erhalte ich unvorhersehbare Werte :smile:. Scheinbar muss es zwingend char * sein.

Jetzt zu der Programmierweise.
Bin Anfänger und bedanke mich bei euch für die Hinweise darauf Fehler abzufangen, Parameter und Funktionsrückgaben zu prüfen.
Meine Bitte: ergänzt mein Programm mit den Prüfungen die ihr machen würdet. Ich werde dann versuchen das „Prinzip“ zu verstehen.

Eventuell wäre es noch von Vorteil die erhaltenen Startparameter vor der

Konvertierung auf ihren Datentyp zu überprüfen

ob die Verwendung von Werten überhaupt erlaubt war

@Allesquatsch. Wie mache ich das:

die Compiler-Warnings streng einzustellen

Vitali

Wie beim Russisch Roulette
Hallo Vitali

Nachdem ich das *.c File unter Knoppix, wie geschrieben,
kompilieren konnte und die Aufgabe zu Ende brachte, versuchte
ich dieses File erneut unter Ubuntu zu kompilieren. Und es war
erfolgreich.
Ich hatte zu 100% ausgemacht, dass der Einsatz eines Wertes
(12) statt der Variablen shmsize (ergab auch 12) die
Das einzige was jetzt anders ist: Ubuntu
neu gestartet (arbeite mit Ubuntu von CD). Seltsam.

Böse Falle. Dass sich etwas erfolgreich kompilieren und ausführen lässt bedeutet nicht, dass es richtig ist. Beim Russisch Roulette macht es auch öfter KLICK als PENG.
Entsprechend ist die das Nichtfunktionieren bei kleineren Änderungen schon ein Hinweis darauf, dass das Programm nur zufällig funktioniert
hat.

Mein Programmierlehrer hatte entsprechend die Anforderung, dass zu jeder erfolgreich abgegebenen Programmierarbeit ein Testprogramm gehörte, welches die Programmierarbeit mit verschiedenen Testdaten befüttern konnte.

Gerade in größeren Programmen ist ein Trial-and-Error-Vorgehen tödlich, weil mit der Komplexität von Programmen auch die Wahrscheinlichkeit wächst, dass unsaubere Verwendung von Speicher zu unvorhersagbaren Ergebnissen führt.

Bin Anfänger und bedanke mich bei euch für die Hinweise darauf
Fehler abzufangen, Parameter und Funktionsrückgaben zu prüfen.
Meine Bitte: ergänzt mein Programm mit den Prüfungen die ihr
machen würdet. Ich werde dann versuchen das „Prinzip“ zu
verstehen.

Grundsätzlich liefern alle Funktionsaufrufe einen Rückgabewert. Ob man Strings in Zahlen wandelt, Speicher anfordert, Dateien öffnet, Daten einliest, Speicherbereiche kopiert. Dazu immer in die Dokumentation (man) der entsprechenden Bibliotheksfunktion schauen. Meist bringen das aktuelle Entwicklungsumgebungen schon im Editor mit, dass man die Referenz angezeigt bekommt.

Wenn die Funktion sehr einfach ist und z.B. nur einen Integer zurückgibt, können manche Werte auch für Fehlermeldungen stehen: Bei atoi z.B. kommt die 0 auch, wenn es keine Zahl ist. INT_MAX oder INT_MIN stehen für Wertebereichsüberschreitungen.
Wenn Du’s nicht abtestest, kann ein zufälliger Text in einen int gewandelt also dazu führen, dass Du mit INT_MAX weiterrechnest und dann entsprechend Speicher allokieren willst.

An anderes muss man einfach selbst denken. Beispielsweise dass sichergestellt ist, dass bei Kopieren von Strings das Ziel auch ausreichend groß ist. Im Zweifel lieber strncpy nutzen.

Beispiel:

#define MAX\_ANZ\_ZEICHEN 10
 char str1[]= "Der Text zu lang oder Du weißt nicht wie lang";
 char str2[MAX\_ANZ\_ZEICHEN+1];
 strncpy (str2,str1,MAX\_ANZ\_ZEICHEN);
 str2[MAX\_ANZ\_ZEICHEN]='\0'; // Ist einfach als zu prüfen, ob der Text kürzer war

die Compiler-Warnings streng einzustellen

@Allesquatsch. Wie mache ich das:

Ist normalerweise in der Entwicklungsumgebung konfigurierbar. Sicherlich findest Du was, wenn Du nach Deinen konkret genutzten Werkzeugen googlest. Meine persönliche Praxis liegt Jahrzehnte zurück, aber gerade deshalb gehe ich davon aus, dass es heute noch einfacher zu konfigurieren ist. Der Compiler selbst bekam dann eine lange Liste von Optionen mitgeteilt, die man in der Entwicklungsumgebung per Stufe oder per Einzelmeldung konfigurieren konnte.
Das war schon ein Quantensprung gegenüber den Ursprüngen, als es mit lint ein extra Programm zur Vorprüfung von Code gab und der Compiler stur ohne große Prüfung umsetzte.

Ciao, Allesquatsch

Hallo Vitali,

zunächst einmal: Wir könnten dir sicherlich alle Prüfungen und Validierungen die wir vornehmen würden in deinen Code einfügen, aber ich denke in deinem Interesse wäre es lehrreicher, du kümmerst dich da selbst drum :wink:. Unterstützung und Beratung biete ich dir aber an.

Björn, bitte um Erklärung wie mir die errno bei der
Kompilierung hilft. Kenne die strerror() und errno nur vom
Sehen in fremden Scripten hier im Forum, aber meiner Meinung
nach helfen diese nur wenn das Programm läuft. Oder?

Um die errno zu verwenden musst du zunächst „errno.h“ einbinden. Tritt nun (in deinem Fall) bei Verwendung von shmget ein Fehler bei der Segmentierung auf, enthält errno den entsprechenden Fehlercode. Diesen kannst du, wie du selbst schon richig erkannt hast nur zur Laufzeit verwenden und ausgeben lassen. Wenn sich das Programm erst gar nicht kompilieren lässt hilft dir errno nicht weiter. In dem Fall kannst du nur die Compilermeldung auswerten und entweder über deine Erfahrung auf den Fehler stoßen oder (was bei Anfängern oftmals hilft) den Compilerfehlercode googeln, bzw. bei Verwendung von Visual Studio diesen im msdn nachschauen.

Wollte schlau sein und mein main wie folgt angehen: main (int
argc, int *argv[])
. Beim auslesen von argv[] aber erhalte ich
unvorhersehbare Werte :smile:. Scheinbar muss es zwingend char *
sein.

Die Main Methode eines C-Programmes kann nur ohne Übergabeparameter „main(void)“ oder mit Übergabeparameter in der Form „main(int argc, char *argv[])“ deklariert werden. Das das an den unvorhersehbaren Datentypen der Startparameter liegt hast du bereits richtig erkannt.

Meine Bitte: ergänzt mein Programm mit den Prüfungen die ihr
machen würdet. Ich werde dann versuchen das „Prinzip“ zu
verstehen.

Wie bereits oben gesagt geb ich dir hier nur ein paar Tipps, damit du selber aktiv werden kannst.

  1. Wie Allesquatsch bereits erwähnt hat, wäre der erste Schritt das Überprüfen der Anzahl der Übergabeparamter. Diese ist im Übergabeparameter „int argc“ der main methode enthalten.

  2. Überprüfen der Datentypen. In C ist das leider nicht ganz so einfach wie in moderneren Programmiersprachen. Eine Möglichkeit wäre es, durch die einzelnen Zeichen deiner Parameter zu iterieren um jedes Zeichen auf eine Zahl zu prüfen.

z.B.:

#include

for (int i = 0; i " googeln.

Viel Erfolg

Gruß Björn