[ LUGOS ] Summary: Signali in paralelno procesiranje...

Boris Benko boris.benko na telekom.si
Pon Jul 20 10:02:29 CEST 1998


Zdravo!

Neko"c sem vpra"sal o signalih, pa sem dobil namig (ki bi ga sicer lahko dobil
tudi iz manuala, ampak
jaz sem takrat bolj pri"cakoval kak"sno kodo), kako stvari reševati. Vmes sem
napisal en program"cek,
ki ga pripenjam kot attachment, ki demonstrira paralelno izvajanje in lovljenje
signalov.

Opis problema:

Imam program, pisan v Cju, ki v "stirih urah (groba ocena) predela podatke in jih
zapi"se na izhod.
Podatke tik po tem, ko jih zapi"se z system komando (gzip) skompresira. Ko
kompresira, "caka, da
se bo kompresija kon"cala. "Ceprav bi vmes lahko "ze bral naslednji set vhodnih
podatkov in stvari
obdeloval naprej. Disk, s katerega bere podatke je drugi, kot tisti, na katerega
pi"se. Ideja je bila,
da glavni program spawna otroke, ki spro"zijo kompresijo, sam glavni program pa ta
"cas melje naprej.

Kot stranski problem se je potem pojavilo, da "stevilo hkratnih kompresij ne sme
biti preveliko, da ne
zadu"si CPUjev in izhodnega diska. Dovoljene bi bile recimo tri hkratne background
kompresije.

Zadeva se da re"siti tako, da glavni program instalira svoj signal handler za tip
signala SIGCLD.
Funkcija signal(), ki je sicer ANSI, ni uporabna. In sicer zato ne, ker se po
klicu handlerja instalira
defaultni handler. Sicer se lahko po klicu handlerja ponovno instalira handler,
vendar obstaja mo"znost,
da se bo kon"canje kak"snega otroka izpustilo. Res je, da ce uporabis BSD alike
signal funkcijo, da potem
se NE INSTALIRA DEFAULT HANDLER. To pa pomeni, da na neki drugi masini pomotoma
napak
uporabim knjiznico pri linkanju (recimo na Solarisu) in se program sicer prav
prevede, dela pa ne.

The way to go je bila funkcija sigaction(). Za pregled, koliko otrok se je
koncalo, pa waitpid().
Iz kode se vse vidi. Dodal bi se, da me je mucil en nasty bug.

Vsi so mi priporocali kodo v obliki:
do
{
   ret_pid=waitpid();
}while(ret_pid);

To je napak. Mora biti while(ret_pid>0), ker waitpid vrne -1, ce ni vec otrok.

Mucilo me je se par vprasanj. Recimo, ali je childs_active++ in childs_active--
atomska operacija?
Zakaj gre? Ce je recimo vrednost childs_active 5 in program ravno zeli spawnati se
enega childa,
potem zeli povecati childs_active za ena. Ampak v tistem trenutku pride signal in
v handlerju se
vrednost zmanjsa za eno. Pravilna vrednost childs_active je torej 4. Ampak glavni
program to
vrednost zveca na 6 in ne na pravilnih 5.

Dodal bi se, da je sinhronizacija med otroki in starsi minimalna. Otrok dobi ime
datoteke in siba kompresijo,
nato pa se konca. Ni nobene med procesne komunikacije, nic. Mogoce tudi velja
povedati, da je
tako paralelno delo smiselno samo tam, kjer je zadosti CPUja in diskov, da
paralelno stvari res
gredo hitreje. Ce je en sam IDE disk in kaksna malo slabsa CPU, nima smisla
komplicirati.
Ce pa sta CPUja dva in diska razlicna pa so prihranki znatni.

Program pzip uporabljate tako, da mu podate set datotek, ki jih mora paralelno
skompresirati.
Lahko mu za test podate 50 (praznih!) datotek, lahko recimo 1 dolgo, ter 20
kratkih, ter recimo
10 praznih. Da se vidi, kako program reagira na omejitev, koliko hkratnih
kompresij lahko tece.

Any comments?

=b

--
============================================================================
Boris Benko, dipl.ing.                | E-mail: Boris.Benko na telekom.si
Telekom Slovenije, PE Murska Sobota   |         B.Benko na s-gms.ms.edus.si
Senior Prog./Sys admin./Informatik    |         Boris.Benko na computer.org
Slu§ba za informatiko                 | Phone: +386 69 31 676
                                      | ISDN:  +386 69 14 632
============================================================================


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define MAX_CHILDS	2
#define GZIP_BINARY	"/bin/gzip"

int childs_spawned;
int childs_returned;
int intr_count;
int handler_looped;
struct sigaction oldhand;

void spawn_child(char *ime);
void child_handler(int signo);

int main(int argc,char *argv[])
{
	int status;
	struct sigaction sact;
	int i;
	int ret_pid;

	childs_spawned=0;
	childs_returned=0;
	intr_count=0;
	handler_looped=0;
	sact.sa_handler=child_handler;
	if(sigemptyset(&sact.sa_mask)!=0)
	{
		printf("Error installing signal handler!\n");
		return 1;
	}
	sact.sa_mask|=SIGCLD;
	sact.sa_flags=SA_NOCLDSTOP;
	sact.sa_restorer=NULL;

	if(sigaction(SIGCLD,&sact,&oldhand)==-1)
	{
		printf("Error(2) installing signal handler!\n");
		return 2;
	}

	for(i=1;i<argc;i++)
	{
		if(childs_spawned-childs_returned>MAX_CHILDS)
		{
			printf("Waiting for children to terminate...\n");
			waitpid(-1,NULL,0);childs_returned++;
			printf("One child terminated, proceeding...\n");
		}
		printf("Spawning compression with datasource: %s (count: %d)\n",
			argv[i],childs_spawned-childs_returned);
		spawn_child(argv[i]);
	}

	/* Po"cakaj "se na ostale otroke	*/
	do 
	{
		ret_pid=waitpid(-1,NULL,0);
		if(ret_pid>0)
		{
			printf("Child terminated...\n");childs_returned++;
		}
	}while(ret_pid>0);

	printf("Child count is: %d\n",childs_spawned-childs_returned);
	printf("Interrupt handler was called: %d times.\n",intr_count);
	return 0;
}/* int main()	*/

void spawn_child(char *ime)
{
	struct sigaction dummy;
	char system_command[1024];
	int return_value;
	int fork_value;

	fork_value=fork();
	if(fork_value!=0)
	{
		if(fork_value==-1) printf("Error: Error while forking!\n");
		else childs_spawned++;
	}else
	{
		if(sigaction(SIGCLD,&oldhand,&dummy)==-1)
		{
			printf("Error(2) installing signal handler!\n");
			return;
		}
		strncpy(system_command,GZIP_BINARY,1024);
		strncat(system_command," -f ",1024-strlen(system_command));
		strncat(system_command,ime,1024-strlen(system_command));
		return_value=system(system_command);
		if(return_value!=0) 
			printf("Warning: System returned !=0 value with arg: %s\n",ime);
		exit(0);
	}
	return;
}/* void spawn_child()	*/

void child_handler(int signo)
{
	int status;
	int ret_pid;

	intr_count++;
	do 
	{
		ret_pid=waitpid(-1,&status,WNOHANG);
		if(ret_pid>0)	childs_returned++;
		handler_looped++;
	}while(ret_pid>0);
	return;
}/* void child_handler()	*/





Dodatne informacije o seznamu Starilist