PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : pthread_join(...): Zugriff auf exit-Status -> Segmentation Fault



Snake85
29-10-2008, 21:21
Hi,
ich simuliere grad das Leser-Schreiber-Problem und möchte es mit Threads und Semaphoren bewältigen.
Allerdings bekomm ich immer ein Segmentation Fault (core dumped), wenn ich auch die exit-Status-Variable zugreifen will, die ich bei pthread_join(...) angegeben habe:

In dem Bereich unter
/* auf alle Thread-Ende warten und Exit-Status ausgeben */
genau bei printf("Exit-Status Thread %i: %i\n", tid[i], *status[i]);
Vermutlich muss ich Speicher anfordern im Zusammenhang mit *status[i], aber ich komm einfach nicht auf die Lösung wie, wo und wieviel.



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <synch.h>
#include <unistd.h>
#include "error.h"

/* zu Debug-Szecken */
#define DEBUG 1

/* Groesse der Datenbank */
#define DB_SIZE 5
#define WRITER_INCREASE 10
#define MAX_SLEEP_TIME 5
#define LOOPS 4

/* globale Datenbank (Zahlenfeld) */
int database[DB_SIZE];

/* Schutz der Datenbank */
sema_t db;
/* Schutz der Variablen rc */
pthread_mutex_t mutex;
/* Zahl der Leser in Datenbank */
int grc;

/* Funktionsprototypen */
int initDB();
void destroyDB();
void *reader(void *arg);
void *writer(void *arg);

int main(int argc, char* argv[]) {
pthread_t *tid;
int rc, i, cntReader, cntWriter, cntTid;
int **status;
/* Argumentenanzahl ueberpruefen */
if(argc != 3) {
error_exit("usage: ./rwcoop #reader #writer", 10);
}
/* Datenbank initialisieren */
initDB();
/* Argumente verarbeiten */
cntReader = atoi(argv[1]);
if(cntReader < 0) {
error_exit("error: incorrect number of readers", 20);
}
cntWriter = atoi(argv[2]);
if(cntWriter < 0) {
error_exit("error: incorrect number of writers", 21);
}
/* reader starten */
for(i=0; i<cntReader; i++) {
/* Thread erzeugen, reader aufrufen */
tid[cntTid] = (pthread_t)malloc(sizeof(pthread_t));
rc = pthread_create(&(tid[cntTid]), NULL, reader, &(tid[cntTid]));
cntTid++;
if(rc != 0) {
error_exit("error creating reader thread", 30);
}
}
/* writer starten */
for(i=0; i<cntWriter; i++) {
/* Thread erzeugen, writer aufrufen */
tid[cntTid] = (pthread_t)malloc(sizeof(pthread_t));
rc = pthread_create(&(tid[cntTid]), NULL, writer, &(tid[cntTid]));
cntTid++;
if(rc != 0) {
error_exit("error creating writer thread", 50);
}
}
/* jedem Thread seinen Platz fuer Exit-Status zusichern */
status = malloc(cntTid*sizeof(int));
/* auf alle Thread-Ende warten und Exit-Status ausgeben */
for(i=0; i<cntTid; i++) {
/* auf Threadende warten */
if(DEBUG) printf("1\n");
status[i] = malloc(sizeof(int));
if(DEBUG) printf("1,5\n");
rc = pthread_join(tid[i], (void **)&status[i]);
if(DEBUG) printf("2\n");
if(rc != 0) {
error_exit("error joining threads", i);
}
/* Exit-Status des Threads ausgeben */
if(DEBUG) printf("2,5\n");
printf("Exit-Status Thread %i: %i\n", tid[i], *status[i]);
/* angeforderten Speicher wieder freigeben */
if(DEBUG) printf("3\n");
free(&tid[i]);
if(DEBUG) printf("3,5\n");
/* free(status[i]);*/
if(DEBUG) printf("4\n");
}
/* Datenbank aufloesen: Mutex, Semaphore freigeben */
if(DEBUG) printf("5\n");
destroyDB();
/* free(status);*/
if(DEBUG) printf("6\n");
/* THE END */
return 1;
}

int initDB() {
int rc, i;
/*Mutex initialisieren */
rc = pthread_mutex_init(&mutex, NULL);
if(rc != 0) {
error_exit("error initialising mutex", 60);
}
/* Semaphore initialisieren */
rc = sema_init(&db, 1, USYNC_THREAD, NULL);
if(rc != 0) {
error_exit("error initialising semaphore",62);
}
/* Datenbank initialisieren*/
for(i=0; i<DB_SIZE; i++) {
database[i] = i;
}
/* Datenbankgroesse zurueckgeben */
return sizeof(database)/sizeof(int);
}

void destroyDB() {
int rc;
/* Mutex freigeben */
rc = pthread_mutex_destroy(&mutex);
if(rc != 0) {
error_msg("error destroying mutex", 65);
}
/* Semaphore freigeben */
rc = sema_destroy(&db);
if(rc != 0) {
error_msg("error destroying semaphore", 66);
}
}

void *reader(void *arg) {
int rc, i, nr;
unsigned int pause;
/* Argument auswerten */
if(DEBUG) printf("7\n");
nr = *(int *)arg;
if(DEBUG) printf("8\n");
/* Datenbank ausgeben */
for(i=0; i<DB_SIZE; i++) {
if(DEBUG) printf("9\n");
/* wait(mutex), Zugriff auf Zaehler sperren */
pthread_mutex_lock(&mutex);
if(DEBUG) printf("10\n");
/* Zahl der Leser hochzaehlen */
grc++;
if(DEBUG) printf("11\n");
if(grc == 1) {
/* wait(db), Schreiber aussperren, falls dies der erste Leser */
if(DEBUG) printf("12\n");
sema_wait(&db);
if(DEBUG) printf("13\n");
}
/* signal(mutex), Zugriff auf Zaehler freigeben */
pthread_mutex_unlock(&mutex);
if(DEBUG) printf("14\n");

/* lesenden Datenbankzugriff ausfuehren */
printf("Thread %i reads position %i: %i\n", nr, i, database[i]);

/* wait(mutex), Zugriff auf Zaehler sperren */
if(DEBUG) printf("15\n");
pthread_mutex_lock(&mutex);
if(DEBUG) printf("16\n");
/* Zahl der Leser runterzaehlen */
grc--;
if(grc == 0) {
/* signal(db), Schreiber zulassen, falls dies der letzte Leser */
if(DEBUG) printf("17\n");
sema_post(&db);
if(DEBUG) printf("18\n");
}
/* signal(mutex), Zugriff auf Zaehler freigeben */
if(DEBUG) printf("19\n");
pthread_mutex_unlock(&mutex);
if(DEBUG) printf("20\n");

/* kleine Pause einlegen */
if(DEBUG) printf("21\n");
pause = (unsigned int)(rand()%MAX_SLEEP_TIME);
printf("Thread %i sleeps %i seconds\n", nr, pause);
rc = sleep(pause);
if(rc > 0) {
error_msg("error, i'm still a tired reader", 70);
}
if(DEBUG) printf("22\n");
}
/* Thread beenden */
rc = 42;
if(DEBUG) printf("23\n");
pthread_exit((void *)&rc);
}

void *writer(void *arg) {
int rc, i, nr;
unsigned int pause;
/* Argument auswerten */
if(DEBUG) printf("24\n");
nr = *(int *)arg;
if(DEBUG) printf("25\n");
/* Datenbank manipulieren */
for(i=0; i<DB_SIZE; i++) {
if(DEBUG) printf("26\n");
/* wait(db) */
sema_wait(&db);
if(DEBUG) printf("27\n");

/* schreibenden Datenbankzugriff ausfuehren */
if(DEBUG) printf("28\n");
database[i] += WRITER_INCREASE;
if(DEBUG) printf("29\n");
printf("Thread %i writes to position %i: %i\n", nr, i, database[i]);

/* signal(db) */
if(DEBUG) printf("30\n");
sema_post(&db);
if(DEBUG) printf("31\n");
/* kleine Pause einlegen */
pause = (unsigned int)(rand()%MAX_SLEEP_TIME);
printf("Thread %i sleeps %i seconds\n", nr, pause);
rc = sleep(pause);
if(rc > 0) {
error_msg("error, i'm still a tired writer", 90);
}
if(DEBUG) printf("32\n");
}
/* Thread beenden */
rc = 4711;
if(DEBUG) printf("33\n");
pthread_exit((void *)&rc);
}


Sorry, für den Haufen printf's, aber so sieht man wo der Fehler begraben liegt ;)

panzi
29-10-2008, 21:37
Du kannst nicht die Adresse von etwas, das am Stack liegt zurückliefern denn der Stack wird ja nachdem die Methode beendet wird (was bei pthread_exit ja passiert) abgebaut. In der tat wird da der gesamte Stack des Threads freigegeben, da der Thread ja beendet wird. Du musst einen Speicher am Heap zurück geben, also etwas das mit malloc angelegt wurde. Oder die Adresse einer globalen variable. Aber die wär eh auch von einer anderen Stelle aus sehbar und so mit bräuchtest da die Adresse net zurückgeben.

Snake85
30-10-2008, 21:39
Mh, das stimmt anscheinend nicht ganz. Das OS kümmert sich darum, dass die Rückgabe des Thread-Exit nachher noch zugänglich ist. Wäre auch komisch, wenn die Funktionalität theoretisch gegeben wird, aber damits klappt man einen kompletten Work-around zu bauen.

Der Fehler lag an der vermuteten Stelle, so gehts:

int status;
pthread_join(tid[i], (void **)&status);
printf("Exit-Status Thread %i: %i\n", tid[i], status);

Und hier nochmal der gesamte Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <synch.h>
#include <unistd.h>
#include "error.h"

/* zu Debug-Szecken */
#define DEBUG 0

/* Groesse der Datenbank */
#define DB_SIZE 5
#define WRITER_INCREASE 10
#define MAX_SLEEP_TIME 5
#define LOOPS 4

/* globale Datenbank (Zahlenfeld) */
int database[DB_SIZE];

/* Schutz der Datenbank */
sema_t db;
sema_t read;
/* Schutz der Variablen rc */
pthread_mutex_t mutex;
/* Zahl der Leser in Datenbank */
int grc;

/* Funktionsprototypen */
int initDB();
void destroyDB();
void *reader(void *arg);
void *writer(void *arg);

int main(int argc, char* argv[]) {
pthread_t *tid;
int rc, i, j, cntReader, cntWriter, cntTid;
int status;
/* Argumentenanzahl ueberpruefen */
if(argc != 3) {
error_exit("usage: ./rwcoop #reader #writer", 10);
}
/* Datenbank initialisieren */
initDB();
/* Argumente verarbeiten */
cntReader = atoi(argv[1]);
if(cntReader < 0) {
error_exit("error: incorrect number of readers", 20);
}
cntWriter = atoi(argv[2]);
if(cntWriter < 0) {
error_exit("error: incorrect number of writers", 21);
}
/* reader starten */
for(i=0; i<cntReader; i++) {
/* Thread erzeugen, reader aufrufen */
tid[cntTid] = (pthread_t)malloc(sizeof(pthread_t));
rc = pthread_create(&(tid[cntTid]), NULL, reader, &(tid[cntTid]));
cntTid++;
if(rc != 0) {
error_exit("error creating reader thread", 30);
}
}
/* writer starten */
for(i=0; i<cntWriter; i++) {
/* Thread erzeugen, writer aufrufen */
tid[cntTid] = (pthread_t)malloc(sizeof(pthread_t));
rc = pthread_create(&(tid[cntTid]), NULL, writer, &(tid[cntTid]));
cntTid++;
if(rc != 0) {
error_exit("error creating writer thread", 50);
}
}
/* auf alle Thread-Ende warten und Exit-Status ausgeben */
for(i=0; i<cntTid; i++) {
/* auf Threadende warten */
if(DEBUG) printf("1\n");
rc = pthread_join(tid[i], (void **)&status);
if(DEBUG) printf("2\n");
if(rc != 0) {
error_exit("error joining threads", i);
}
/* Exit-Status des Threads ausgeben */
if(DEBUG) printf("2,5\n");
printf("Exit-Status Thread %i: %i\n", tid[i], status);
/* angeforderten Speicher wieder freigeben */
if(DEBUG) printf("3\n");
free(&tid[i]);
if(DEBUG) printf("4\n");
}
/* Datenbank aufloesen: Mutex, Semaphore freigeben */
if(DEBUG) printf("5\n");
destroyDB();
if(DEBUG) printf("6\n");
/* THE END */
return 1;
}

int initDB() {
int rc, i;
/*Mutex initialisieren */
rc = pthread_mutex_init(&mutex, NULL);
if(rc != 0) {
error_exit("error initialising mutex", 60);
}
/* Semaphore initialisieren */
rc = sema_init(&db, 1, USYNC_THREAD, NULL);
if(rc != 0) {
error_exit("error initialising semaphore db",62);
}
rc = sema_init(&read, 1, USYNC_THREAD, NULL);
if(rc != 0) {
error_exit("error initialising semaphore read",64);
}
/* Datenbank initialisieren*/
for(i=0; i<DB_SIZE; i++) {
database[i] = i;
}
/* Datenbankgroesse zurueckgeben */
return sizeof(database)/sizeof(int);
}

void destroyDB() {
int rc;
/* Mutex freigeben */
rc = pthread_mutex_destroy(&mutex);
if(rc != 0) {
error_msg("error destroying mutex", 65);
}
/* Semaphore freigeben */
rc = sema_destroy(&db);
if(rc != 0) {
error_msg("error destroying semaphore", 66);
}
}

void *reader(void *arg) {
int rc, i, nr;
int rcExit;
unsigned int pause;
/* Argument auswerten */
if(DEBUG) printf("7\n");
nr = *(int *)arg;
if(DEBUG) printf("8\n");
/* Datenbank ausgeben */
for(i=0; i<DB_SIZE; i++) {
if(DEBUG) printf("9\n");
/* wait(mutex), Zugriff auf Zaehler sperren */
pthread_mutex_lock(&mutex);
if(DEBUG) printf("10\n");
/* Zahl der Leser hochzaehlen */
grc++;
if(DEBUG) printf("11\n");
if(grc == 1) {
/* wait(db), Schreiber aussperren, falls dies der erste Leser */
if(DEBUG) printf("12\n");
sema_wait(&db);
if(DEBUG) printf("13\n");
}
/* signal(mutex), Zugriff auf Zaehler freigeben */
pthread_mutex_unlock(&mutex);
if(DEBUG) printf("14\n");

/* lesenden Datenbankzugriff ausfuehren */
printf("Thread %i reads position %i: %i\n", nr, i, database[i]);

/* wait(mutex), Zugriff auf Zaehler sperren */
if(DEBUG) printf("15\n");
pthread_mutex_lock(&mutex);
if(DEBUG) printf("16\n");
/* Zahl der Leser runterzaehlen */
grc--;
if(grc == 0) {
/* signal(db), Schreiber zulassen, falls dies der letzte Leser */
if(DEBUG) printf("17\n");
sema_post(&db);
if(DEBUG) printf("18\n");
}
/* signal(mutex), Zugriff auf Zaehler freigeben */
if(DEBUG) printf("19\n");
pthread_mutex_unlock(&mutex);
if(DEBUG) printf("20\n");

/* kleine Pause einlegen */
if(DEBUG) printf("21\n");
pause = (unsigned int)(rand()%MAX_SLEEP_TIME);
printf("Thread %i sleeps %i seconds\n", nr, pause);
rc = sleep(pause);
if(rc > 0) {
error_msg("error, i'm still a tired reader", 70);
}
if(DEBUG) printf("22\n");
}
/* Thread beenden */
rcExit = 42;
if(DEBUG) printf("23\n");
pthread_exit((void *)rcExit);
}

void *writer(void *arg) {
int rc, i, nr;
int rcExit;
unsigned int pause;
/* Argument auswerten */
if(DEBUG) printf("24\n");
nr = *(int *)arg;
if(DEBUG) printf("25\n");
/* Datenbank manipulieren */
for(i=0; i<DB_SIZE; i++) {
if(DEBUG) printf("26\n");
/* wait(db) */
sema_wait(&db);
if(DEBUG) printf("27\n");

/* schreibenden Datenbankzugriff ausfuehren */
if(DEBUG) printf("28\n");
database[i] += WRITER_INCREASE;
if(DEBUG) printf("29\n");
printf("Thread %i writes to position %i: %i\n", nr, i, database[i]);

/* signal(db) */
if(DEBUG) printf("30\n");
sema_post(&db);
if(DEBUG) printf("31\n");
/* kleine Pause einlegen */
pause = (unsigned int)(rand()%MAX_SLEEP_TIME);
printf("Thread %i sleeps %i seconds\n", nr, pause);
rc = sleep(pause);
if(rc > 0) {
error_msg("error, i'm still a tired writer", 90);
}
if(DEBUG) printf("32\n");
}
/* Thread beenden */
rcExit = 11;
if(DEBUG) printf("33\n");
pthread_exit((void *)rcExit);
}

locus vivendi
31-10-2008, 13:41
[...] Das OS kümmert sich darum, dass die Rückgabe des Thread-Exit nachher noch zugänglich ist.
Das stimmt soweit. Panzi hatte aber trotzdem recht. Die Posix Implementierung speichert nur den Wert des Zeigers selber. Wenn du den Zeiger verwenden willst, muss auch der Speicher selber noch da sein (mal abgesehen vom Fall in dem der Zeiger 0 ist).

Deine Programm hast du jetzt verschlimmbessert. Du gibst an der besprochenen Stelle zwar keinen Zeiger mehr raus, der ungültig wird, aber dafür castest du zwischen "int" und "void*", was kein portables C oder C++ ist.

panzi
02-11-2008, 23:37
Off Topic:

... aber dafür castest du zwischen "int" und "void*", was kein portables C oder C++ ist.
Was der einzige Weg zu sein scheint (http://twoday.tuwien.ac.at/pub/stories/318250/) mit dem man Java-Bindings realisiert.