C++ GPGU Tutorial

ZuseZ3

Lt. Commander
Registriert
Jan. 2014
Beiträge
1.659
Da mich die enormen Leistungen Von GPU`s schon länger interressieren, wollte ich so langsam anfangen erste Programme zu schreiben, welche diese Leistung ausnutzen. Die Theorie kenne ich im groben und ganzen schon und ich weiß auch unter welchen Bedingungen sich GPGU überhaupt lohnt.
Ich würde dafür gerne CUDA C++ nutzen jedoch habe ich noch keine wirklich überzeugenden Tutorials gefunden, die Nvidia Erklärungen sind größtenteils hardwaremäßig. Die beispielprogramme kriege ich bei mir nicht zum laufen da ich in meinem eigenen PC eine AMD graka nutze und lediglich über die Uni bei uns Zugang zu 2 servern mit Nvidia Grakas habe. Ich währe anfangs natürlich volkommen zufrieden damit, eine einzelne der Grafikkarten zu nutzen.
Auf der Nvidia homepage habe ich aber auch eine Übersicht über OpenAcc gefunden, was anscheinend universeller ist und dazu auch noch einfacher zu nutzen sein soll, da man leidiglich kleinere Compileranweisungen in den code schreiben muss.
Sollte ich mich daher evtl lieber mit openAcc befassen? Andererseits habe ich an anderer Stelle gelesen, dass ich auch als developer Mitglied nur einen 2 wöchigen testzugang dazu erhalte. Gibt es da z.B. eine Gratisversion für Schüler oder ähnliche Alternativen ?
Unabhängig davon was ihr mir empfehlt währe ich über empfehlenswerte Tutorials dazu dankbar.
Viele Grüße, manuel
 
Zuletzt bearbeitet:
Für CUDA empfiehlt sich der "CUDA C Programming Guide" von NVIDIA selbst.
 
Wie wärs denn mit einem Emulator? Dann kannst auch daheim debuggen.

link

Kenn mich da auch nicht aus. Also wirst wohl etwas rumlesen müssen, wie man die Emulatoren in die Entwicklungsumgebung integriert.
 
Ich würde mir ungerne die mühe machen einen Extra Emulator einzufügen, da ich normalerweise auch von zuhause aus Zugang zu den servern habe. Mein Problem war lediglich, dass sich die installation der NVidia tools nach der hälfte abgebrochen hat und ich so ohne weiteres nicht zu den Erklärungen/Beispielsprogrammen komme. Onehin war ich nicht so begeistert davon das ganze Nvidia gedöns auf meinen Rechner runterladen zu müssen. Auf den Uniservern laufen die VS Programme auch nicht ohne weiteres da diese natürlich Linux nutzen.
Prinzipiell war ich schon speziell an CUDA interressiert, da dies ja z.T. einfacher (zu lernen) sein soll. Dank nullPtr`s empfehlung werde ich wohl auch erstmal dabei bleiben da ich dann auch auf den uniservern keine Extra compiler installieren muss und meistens ja zugriff darauf habe.
Den Guide muss ich wohl irgendwie übersehen haben, wahrscheinlich war ich zu sehr auf c++ fokusiert.

Ich werde den guide dann erstmal durcharbeiten, sobald ich Fragen habe melde ich mich wieder. und nochmal danke an alle :)
 
Wie wärs mit dem Kurs von Udacity: https://www.udacity.com/course/cs344

Fand ich ziemlich gut als ich den vor einem Jahr gemacht habe, und man hat damit die Möglichkeit seinen Code auf der Amazon Cloud laufen zu lassen, also von überall Zugriff auf Nvidia Grafikkarten. Der Kurs beginnt bei den Basics und die Aufgaben sind alle simple Sachen aus dem Bereich Image Processing. Man lernt auch wirklich den Code effizient zu machen usw. Wenn du mit dem Udacity Stil (Videotutorials) etwas anfangen kannst, ist der auf jeden Fall zu empfehlen
 
Danke, ich habe gerade die erste lektion größtenteils durch und spontan versucht ein älteres Python multicore Programm für Cuda umzuschreiben. Es währe nett falls ihr mir die Fehlermeldungen erklären könntet.

Code:
#include <stdio.h>

__global__ void collatz(float * d_out, float * d_in)
{
 int idx = threadIdx.x;
 float collnumber = d_in[idx];
 float steps = 0;
 while (collnumber > 1)
{
 if (int(collnumber) % 2 == 0){collnumber /= 2;}
 else {collnumber *= 3; collnumber += 1;}
 steps += 1;
}
 d_out[idx] = steps;
}

int main(int argc, char** argv) {
const int ARRAY_SIZE = 128;
const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);

float h_in[ARRAY_SIZE];
for(int i = 0; i < ARRAY_SIZE; i++){
 h_in[i] = float(i);
}
float h_out[ARRAY_SIZE];


float * d_in;
float * d_out;

cudaMalloc((void**) &d_in, ARRAY_BYTES);
cudaMalloc((void**) &d_out, ARRAY_BYTES);


cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice);

collatz<<<1, ARRAY_SIZE>>>(d_out, d_in);

cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);


cudaFree(d_in);
cudaFree(d_out);

	for (int i =0; i < ARRAY_SIZE; i++) {
		printf("%f", h_out[i]);
		printf(((i % 4) != 3) ? "\t" : "\n"); }
}
:
Die Fehler beim compilieren habe ich beseitigt :cool_alt:
Jetzt habe ich aber das Problem dass er mich im Anschluss mit Fehlermeldungen bombardiert. Eigentlich laufen auf dem Server CUDA programme, warum er jetzt nicht will verstehe ich nicht. Hier die Ausgabe:
/tmp/tmpxft_00005f64_00000000-14_prob14.o: In function `main':
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x5e): undefined reference to `cudaMalloc'
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x6f): undefined reference to `cudaMalloc'
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x8c): undefined reference to `cudaMemcpy'
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0xe6): undefined reference to `cudaConfigureCall'
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x11a): undefined reference to `cudaMemcpy'
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x126): undefined reference to `cudaFree'
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x132): undefined reference to `cudaFree'
/tmp/tmpxft_00005f64_00000000-14_prob14.o: In function `__cudaUnregisterBinaryUtil()':
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x158): undefined reference to `__cudaUnregisterFatBinary'
/tmp/tmpxft_00005f64_00000000-14_prob14.o: In function `__device_stub__Z7collatzPfS_(float*, float*)':
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x180): undefined reference to `cudaSetupArgument'
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x19f): undefined reference to `cudaSetupArgument'
/tmp/tmpxft_00005f64_00000000-14_prob14.o: In function `__nv_cudaEntityRegisterCallback(void**)':
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x24b): undefined reference to `__cudaRegisterFunction'
/tmp/tmpxft_00005f64_00000000-14_prob14.o: In function `__sti____cudaRegisterAll_41_tmpxft_00005f64_00000000_6_prob14_cpp1_ii_c48ceada()':
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text+0x25f): undefined reference to `__cudaRegisterFatBinary'
/tmp/tmpxft_00005f64_00000000-14_prob14.o: In function `cudaError cudaLaunch<char>(char*)':
tmpxft_00005f64_00000000-3_prob14.cudafe1.cpp:(.text._Z10cudaLaunchIcE9cudaErrorPT_[_Z10cudaLaunchIcE9cudaErrorPT_]+0x14): undefined reference to `cudaLaunch'
collect2: error: ld returned 1 exit status

Auf den Udacity servern läuft das Programm dagegen. Wahrscheinlich sollte ich mal jemanden von der hießigen Uni anschreiben oder weiß jemand von euch spontan woran genau es liegen könnte.
 
Zuletzt bearbeitet:
Du mußt gegen die Library, in der die vermißten Funktionen definiert sind, linken. Wenn die Funtionen z.B. in cudalib.a drin wären, dann mußt du die Linker-Kommandozeile um -lcudalib.a erweitern.
 
Danke, mit -lcudart läuft es.
Ergänzung ()

Hier ist die aktuellste Version sie läuft mitlerweile schon auf mehreren kernen und dient nebenbei als online sicherungskopie. Leider funktioniert sie nur bei kleineren werten bei einer Million erhalte ich bereits falsche Werte aufgrund von overflows. Leider kann ich float nicht ersetzten da der Compiler sonst meckert. Hat jemand Erfahrungen was das angeht? GPU`s sind ja onehin auf float berechnungen ausgelegt bei anderen typen dürften sie also langsamer sein ?!
Ich habe da bisher noch nichts zu gefunden.
Code:
    #include <stdio.h>

    const int kernelnr = 1000;
    const int threadsPerBlock = 1000;
    const int ARRAY_SIZE = kernelnr * threadsPerBlock;
    __global__ void collatz(float * d_out, float * d_in)
    {
    int idx = threadIdx.x + blockIdx.x * threadsPerBlock;
    float collnumber = d_in[idx];
    float steps = 0;
    while (collnumber > 1)
    {
    if (int(collnumber) % 2 == 0){collnumber /= 2;}
    else {collnumber *= 3; collnumber += 1;}
    steps += 1;
    }
    d_out[idx] = steps;
    }

    int main(int argc, char** argv) {
    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);

    float h_in[ARRAY_SIZE];
    for(int i = 0; i < ARRAY_SIZE; i++){
    h_in[i] = float(i);
    }
    float h_out[ARRAY_SIZE];


    float * d_in;
    float * d_out;

    cudaMalloc((void**) &d_in, ARRAY_BYTES);
    cudaMalloc((void**) &d_out, ARRAY_BYTES);

    cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice);

    collatz<<<kernelnr, threadsPerBlock>>>(d_out, d_in);

    cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);

    int maxnumber = 0;
    int longeststeps = 0;

    for (double i = 0; i < ARRAY_SIZE; i++) {
    if (h_out[i] > longeststeps) {longeststeps = h_out[i]; maxnumber = i + 1;}
    }
    printf("Number : ");
    printf("%i",maxnumber);
    printf("\n");
    printf("steps : ");
    printf("%i",longeststeps);
    printf("\n");
    printf("%i", ARRAY_SIZE);
    printf("\n");

    cudaFree(d_in);
    cudaFree(d_out);

    /*double result = 837799;
    int i = 0;
    while (int(result) > 1)
    { if(int(result) % 2 == 0) result /= 2;
      else {result *=3; result += 1;}
      i += 1; printf("%0.f",result); printf("\n");
    } printf("%i",i);*/
}
 
Zuletzt bearbeitet:
Ich verstehe nicht ganz, warum ich kein Overflow Problem habe. Schlieslich bricht vor allem der letzte auskommentierte Teil willkürlich bei 58 (2974984576) ab nachdem er sich bis dahin Problemlos durchgerechnet hat. Die selbe Rechnung führe ich auf der GPU eine Millionen mal durch und viele diser Rechnungen werden ähnlich große Zahlen nutzen. Demnach dürfte ich zahlreiche overflows haben welche die Ergebnisse verfälschen.

Edit: Was du jetzt mit dem abbilden meinst habe ich mitlerweile verstanden aber mein Overflow Problem bleibt ja.
 
Zuletzt bearbeitet:
Teilweise noch etwas zu vor deinem edit:
1. Ein Integer ist in CUDA wie in den meisten CPP Implementierungen 32 Bit groß also geht er als unsigned bis 4,294,967,296.

2. Um doubles oder neuere CUDA Features in CUDA zu verwenden musst du die Compute Capability im Compiler hochstellen oder mit Defines arbeiten.

3. Sofern doubles nicht merklich schneller sind würde ich lieber 64-Bit integerzahlen verwenden, da diese einen größeren Wertebereich haben. In Cuda heißen sie long long int.

4. Das kein Overflow auftritt war auf deine SP-FP Rechnung bezogen, da ein Float weniger Ganzzahlen darstellen kann als ein Int.
 
Der 4. Punkt ist mir zugegebenermasen erst recht spät aufgefallen.
Was die Compute Capability angeht können die Uni Grakas nur 2.0, ich wollte die doubles onehin nur aufgrund des größeren Wertebereichs.
Was die long long int angeht haben sie nicht geholfen.
An der Stelle 58 springt der Wert von 991661525 auf -1319982720, egal ob ich int, unsigned int oder unsigned long long int nutze, was sich mir nicht ganz erschliest, da alle 3 unterschiedliche Obergrenzen haben und der Overflow daher wenn überhaupt an verschiedenen Stellen auftreten müsste. Könnte es wieder sein das ich mit meinem Programm bereits die richtigen Werte berechnet habe aber sie schlicht und ergreifend falsch ausgebe? Dafürsprechen würde dass es jedesmal bei 58 diesen Sprung gibt. Jedoch habe ich bereits auf verschiedene Arten über printf mittels %ll /%u/%i bzw einer kombination daraus versucht dem Abhilfe zu schaffen was mir jedoch nicht gelungen ist. Für Vorschläge währe ich natürlich dankbar. Ich habe bisher keine Vernünftige Änderungsidee.

Bei kleineren Werten funktioniert das Programm weiterhin, es kann also nicht an einem falschen Algorithmus liegen.
 
Zuletzt bearbeitet:
Was genau hast du durch unsigned long long ersetzt?

Im C++ - Code fällt das Kind in den Brunnen wenn dein double einen Wert > 2^31 erreicht, du aber im Vergleich mit > 1 auf einen int castest, denn der passt da nicht mehr rein. Das ist genau bei Stelle 58 der Fall (der double hat dann 2,974,984,576, was beim casten nach int mit overflow genau -1319982720 ergibt).

Du darfst bei keiner Sub-operation (egal ob in C++ oder CUDA) einen overflow oder sonstige imprecision haben die der Algorithmus nicht korrekt handhabt!
Bei Fließkommazahlen ändert sich das epsilon mit der Werterange. Irgendwannmal wird die Addition von +1 dann zu einer NoOp. Z.b. schlägt (C++)
assert(1e10 + 1 == 1e10);
an, aber
assert(1e20 + 1 == 1e20);
schon nicht mehr. Dadurch würde bei dir der Wechsel von ungerade auf gerade verpasst werden. Bei float schlägt nicht einmal 1e10 mehr an.
Dass floats manche integer Werte nicht exakt darstellen können und man daher - wenn überhaupt - zum nearest integer, und nicht per trimm-decimals casten sollte, ist eine weitere Geschichte. Spielt im Endeffekt aber keine entscheidende Rolle, da es nicht das einzige Problem ist.

Am besten du stellst alles auf unsigned long long um und rechnest nur damit. Und bevor du die Operation y = x *3 + 1 machst musst du auch checken, ob sich das Resultat noch ausgeht (also x <= (MaxValueOfUnsignedLongLong - 1) / 3), ansonsten irgendeine Fehlermeldung.
 
Nochmal die Frage, gpus sind doch eher auf float Rechnungen ausgelegt, oder irre ich mich da? ...
Jedenfalls erhalte ich sobald ich auch die GPU rechnungen auf ull umstelle den Hinweis Segmentation fault..

Ergänzung. Der Code mit long long int`s:

Code:
#include <stdio.h>

const unsigned long long int Blocknr = 1000;
const unsigned long long int threadsPerBlock = 1000;
const unsigned long long int ARRAY_SIZE = Blocknr * threadsPerBlock;

__global__ void collatz(unsigned long long int * d_out, unsigned long long int * d_in)
{
unsigned long long int idx = threadIdx.x + blockIdx.x * threadsPerBlock;
unsigned long long int collnumber = d_in[idx];
unsigned long long int steps = 0;
while (collnumber > 1)
{
if (collnumber % 2 == 0){collnumber /= 2;}
else {collnumber *= 3; collnumber += 1;}
steps += 1;
}
d_out[idx] = steps;
}

int main(int argc, char** argv) {
const unsigned long long int ARRAY_BYTES = ARRAY_SIZE * sizeof(unsigned long long int);

unsigned long long int h_in[ARRAY_SIZE];
for(unsigned long long int i = 0; i < ARRAY_SIZE; i++){
h_in[i] = i;
}
unsigned long long int h_out[ARRAY_SIZE];

unsigned long long int * d_in;
unsigned long long int * d_out;

cudaMalloc((void**) &d_in, ARRAY_BYTES);
cudaMalloc((void**) &d_out, ARRAY_BYTES);

cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice);

collatz<<<Blocknr, threadsPerBlock>>>(d_out, d_in);

cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);

unsigned long long int maxnumber = 0;
int longeststeps = 0;

for (unsigned long long int i = 0; i < 1000000; i++) {
if (h_out[i] > longeststeps) {longeststeps = h_out[i]; maxnumber = i + 1;}
}
printf("Number : ");
printf("%llu",maxnumber);
printf("\n");
printf("steps : ");
printf("%i",longeststeps);
printf("\n");
printf("%llu", ARRAY_SIZE);
printf("\n");
//printf("test : ");
//printf("\n");
// test for (int i = 1; i < 10;){ printf("%ull", h_out[i++]); printf("\n");}

cudaFree(d_in);
cudaFree(d_out);
// Test, 837799 währe das richtige Ergebnis.
unsigned long long int result = 837799;
int i = 0;
printf("%llu", result); printf("\n");
while (result > 1)
{ if(result % 2 == 0) result /= 2;
else {result *=3; result += 1;}
i += 1; printf("%llu",result); printf("\n");
}printf("schritte : "); printf("%i",i); printf("\n");
}

[/spoiler]
 
Zuletzt bearbeitet:
Code:
 unsigned long long int h_in[ARRAY_SIZE];
und
Code:
unsigned long long int h_out[ARRAY_SIZE];

sprengen dir den Stack. So große Blöcke besser mit malloc machen.

Des Weiteren sollten die Thread-Blocks eine Größe von 32*x haben. 256 ist meistens recht gut. Zusätzlich muss man dann im Kernel eine Abfrage einbauen ob man mit dem Thread überhaupt noch Berechnungen durchführen muss.

Wegen der Ausrichtung: Tendentiell ja, siehe den Link, aus meinem letzten Post. Allerdings im Speziellen musst du berücksichtigen, dass du bei deinem Programm viel durch 2 dividierst oder moddest. Bei FP sind beide Operationen relativ teuer, während sie auf Ints relativ günstig sind. (siehe http://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#instruction-optimization )
 
Zuletzt bearbeitet:
Danke für den Hinweis, nach beseitigung eines kleinen Fehler im code und der Umstellung funktioniert es nun für 1 und sogar 10 Millionen Zahlen richtig :cool_alt:
Mein Problem hat sich also endlich erledigt, danke für die Unterstützung.
 
Ich habe hier ein anderes Problem welches ich mir logisch nicht erklären kann.
Es geht um folgendes Problem: https://projecteuler.net/problem=26
Ich speicher im Rest Array die werte übrig die nach der Berechnung übrig bleiben um anschliesend durch die differenz der Array stellen mit dem gleichen Inhalt die länge herauszufinden. Im Programm mache ich das mittels counter - i.
Jedoch setzt er in diesem Programmteil aus unerklärlichen gründen den wert von d_out[idx] von 7 auf null.
einen Overflow kann ich diesesmal ausschliesen. Prinzipiell wird d_out[idx] ja nur unter bestimmten Bedingungen verändert und dann erneut auf den Wert 7 gesetzt. Dass am ende null ausgegeben wird müsste also an irgendeiner Besonderheit von CUDA liegen?
Die ausgabe funktioniert normal, ohne diesen for Teil wird die 7 korrekt ausgegeben.
hier der Problemteil:
Code:
for (; stop == 0; counter++){
 if (number < teiler) {
  number *= 10;
  rest[counter] = number;
 } else {
  number %= teiler;
  rest[counter] = number;
 }
 for (int i = 0;i < counter; i++){
  if (rest[i] == rest[counter]){
   d_out[idx] = 7; stop = 1; (counter - i);
  }
 }
}
Hier der gesammte Code (ich weiß mitlerweile dass 1000 threads ungeschickt sind aber ich änder solche sachen normalerweise erst wenn das Programm an sich vernünftig läuft.)
Code:
#include <stdio.h>
#include <stdlib.h>

const int Blocknr = 1;
const int threadsPerBlock = 1000;
const int ARRAY_SIZE = Blocknr * threadsPerBlock;

__global__ void cycles(int * d_out, int * d_in){

int idx = threadIdx.x;
int rest[50]={};
int counter = 0, number = 1, stop = 0, teiler = idx;
d_out[idx] = 7;
/*
for (; stop == 0; counter++){
 if (number < teiler) {
  number *= 10;
  rest[counter] = number;
 } else {
  number %= teiler;
  rest[counter] = number;
 }
 for (int i = 0;i < counter; i++){
  if (rest[i] == rest[counter]){
   d_out[idx] = 7; stop = 1; (counter - i); // später soll nicht 7 sondern counter -i abgespeichert werden.
  }
 }
}*/ der auskokmmentierte Teil sorgt dafür dass am Ende null statt 7 in d_out steht.
}
int main(int argc, char** argv) {

const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);
int *h_out;
int *h_in;
h_out = (int *) malloc (ARRAY_BYTES);
h_in  = (int *) malloc (ARRAY_BYTES);

int * d_in;
int * d_out;

cudaMalloc((void**) &d_in, ARRAY_BYTES);
cudaMalloc((void**) &d_out, ARRAY_BYTES);

cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice);

cycles<<<Blocknr, threadsPerBlock>>>(d_out, d_in);

cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);

int maxnumber = 0;
int longestcycle = 0;

for (int i = 0; i < 1000; i++) {
if (h_out[i] > longestcycle) {longestcycle = h_out[i]; maxnumber = i;}
}

printf("Number : ");
printf("%i",maxnumber);
printf("\n");
printf("steps : ");
printf("%i",longestcycle);
printf("\n");
printf("%i", ARRAY_SIZE);
printf("\n");
printf("%i",h_out[4]);
printf("\n");

cudaFree(d_in);
cudaFree(d_out);
}
 
CUDA-Kernel brechen bei einer Exception immer ab. Sei es bei der Integer Division durch 0 oder bei einem Segmentation-Fault. Da du den Speicher nicht initialisierst nach deinem cudaMalloc steht eben nach dem Abbruch noch das drinnen was zuvor drinnen stand, was in den aller meisten Fällen 0en sein sollten.

Frage deshalb mal ab ob dein Teiler nie 0 ist und ob die Indexe von Rest immer >= 0 und < 50 sind. Zum einfacheren Debuggen kannst du dir auch mal das Kernel auf die CPU portieren.
 
Zuletzt bearbeitet:
Ich rate dir dazu ein vernünftiges Errorchecking anzuwenden! Gerade bei CUDA bringt es sehr viel, da nicht einfach so zu erkennen warum etwas nicht geht. Auf Stackoverflow ist sehr gut beschrieben wie man für CUDA Errorchecking nutzt.
Sollte dein Kernel fehlerfrei sein (CUDA_SUCCESS), ist algorithmisch noch irgendwas falsch. Z.B. kann die letzte Teil (Verschachtelte for-Schleifen) einfach zu lange dauern und dadurch ein Trebier-Timeout erfolgen, welches den Kernel beendet.
 
Zurück
Oben