/* Fájlnév: osztott-mem-jmp.c

   Ebben a példában együtt szemléltetjük a processz kontextus váltást 
   modellező setjmp( ) és longjmp( ) rendszerhívás párokat és az
   osztott memóriát kezelő rendszerhívásokat. 
   
   A setjmp( ) hívás egy env nevű veremtárba menti a regiszter kontextust 
   és a C-stack-et. Ezt az env veret használja egy későbbi longjmp( ) 
   hívás. Utóbbiból nyilván nincs visszatérés: ugyanis a programszámláló
   regiszter visszavételével a vezérlés visszaugrik a setjmp( ) "belsejébe".
   Ebből következik, hogy a setjmp( )-ből kétszer tér vissza a vezérlés. 
   A két visszatérés a visszatérési értékekkel szétválasztható. 
   
   A példában osztott memória szegmenst kreálunk, ezt a szegmenst leképezzük 
   a processz címtartományára, úgy, hogy az a setjmp-longjmp hívások 
   veremtára lehessen. Lementjük a regiszter kontextust ebbe a verembe (az 
   osztott memóriába), majd gyermek processzt kreálunk. A gyermek processzben 
   - aminek kontextusa majdnem egyezik a szülőjéével - hívjuk meg a longjmp( )-t.

*/
#include <stdio.h>
#include <sys/types.h>
#include <setjmp.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define PERM 	0666
#define SIZE	1024

main( ) {
int		id; 	/* Az osztott memória szegmens leírója. */
jmp_buf *	env;	/* A jmp hívások veremtárához mutató. */
int		vissza; /* A setjmp visszatérési értéke. */
pid_t		pid; 	/* A fork visszatérési értéke. */

	/* Az IPC_PRIVATE kulcsot használom, mert csak e processz 
	   családban használjuk a szegnest.
	   A szegmens mérete a lapméret legyen. */
	if ((id = shmget(IPC_PRIVATE, SIZE, PERM)) < 0) {
		perror(" Nem sikerült az osztott memória szegmens készítése. ");
		exit(-1);	
	}
	
	/* A szegmenst a processz címtartományára "kapcsolom". A szegmenst  
	   a jmp_buf típusú struktúraként kívánjuk látni. 
	   A NULL argumentummal azt kérjük, hogy a rendszer válasszon
	   címtartományt a leképzéshez. */
	if((env = (jmp_buf *)shmat(id, NULL, PERM)) < 0) {
		perror(" Sikertelen rákapcsolódás. ");
		exit(-1);
	}
	
	if ((vissza = setjmp(env)) == 0) { /* Első vissztérés: lementettük a 
		                              regisztertartalmakat. */
		                   
		if(( pid = fork( )) == 0) { /* Itt a gyermek fut. */
		   printf(" A %d azonosítójú gyermek fut. \n", getpid( ));
		
		   /* Visszavesszük a regisztereket. */
		   longjmp(*env, 1);
		   /* Ide a vezérlés nem juthat el. */
		};
		/* Itt a szülő fut. A szülőben nem hívunk longjmp-t. */
		if (waitpid(pid, NULL, NULL)) < 0) { 
			perror(" Gond a szülőben. ");
			exit (-1);
		}
		
		/* Szülőben lekapcsolódunk a szegmensről és exitálunk. */
		shmdt(env);
		exit(0) ;
	} else 
	if (vissza == 1) { 	/* Második visszatérés. Ez csak a gyermekben 
		                   történik meg. */
		printf(" A %d processz a longjmp után.\n", getpid( ));
		
		/* Itt is lekapcsolódunk és exitálunk. */
		shmdt(env);
		exit(0);
	}
}

					
