/******************************************************************************* * * Este programa faz parte do curso sobre tempo real do Laboratorio Embry-Riddle * * Seguem os comentarios originais: * * Experiment #4: Signals, Measureing Signal Dispatch Latency * * Programmer: Eric Sorton * Date: 2/16/97 * For: MSE599, Special Topics Class * * Purpose: The purpose of this program is to measure the time it takes * for a signal to be passed from one process to another. The * scheme for doing this is fairly simple. A parent creates a * shared memory location and two children. The children signal * each other in a ping-pong fashion, storing the times at which * they received the signals into the shared memory. Once they * loop a number of times, they exit, and the parent will tally * the results. * * * Proposito: Através deste programa e possivel medir o tempo que leva * para um sinal ser passado de um processo para outro. O esquema * para realizar isto e bastante simples. Um pai cria uma porcao de * memoria compartilhada e dois filhos. Os filhos sinalizam um ao * outro como um ping-pong, armazenando os tempos em que sentem e * tratam os sinais, na memoria compartilhada. Uma vez que os filhos * repitam isso um numero estabelecido de vezes, eles terminam, e o * pai manipula os resultados. * * *******************************************************************************/ /* * Includes Necessarios */ #include /* for gettimeofday() */ #include /* for gettimeofday(), getpid() */ #include /* for printf() */ #include /* for fork() */ #include /* for wait() */ #include /* for wait() */ #include /* for kill(), sigsuspend(), others */ #include /* for shmget(),shmat(), and shmctl() */ #include /* for shmget(),shmat(), and shmctl() */ /* * NO_OF_ITERATIONS e o numero de sinais que serao enviados. Se este * numero cresce, o tempo de execucao cresce. */ #define NO_OF_ITERATIONS 1000 /* * MICRO_PER_SECOND define o numero de microsegundos em um segundo */ #define MICRO_PER_SECOND 1000000 /* * SHARED_MEMORY_ID, e uma chave arbitraria, correspondendo a um numero * qualquer que deve ser unico. Se alguem estiver usando essa mesma chave, * sera necessario mudar esses numero! */ #define SHARED_MEMORY_ID 1234 /* * A seguinte estrutura define o formato onde ficara armazenada toda a * informacao de tempo coletada pelas criancas. Essa area corresponde a * memoria compartilhada. O ponteiro para essa area esta declarado como * global, de maneira que fica visivel para todos os filhos. Embora o * acesso possa causar contencao, um cuidado especial e tomado de maneira * a garantir que cada filho tenha sua propria area de armazenamento no * trecho que manipula e trata o sinal. O primeiro filho e dono da primeira * linha e o segundo, da segunda. Alem disso, os sigmasks (mascaras de sinais) * sao estabelecidos de maneira a nao permitir que multiplos manipuladores de * sinais acessem a area ao mesmo tempo. * * Além da informacao de tempo, sera armazenado o pid do filho, de maneira a * permitir que um filho saiba da identificacao do irmao que ira sinalizar. * */ typedef struct { pid_t child_pid[2]; struct timeval timings[NO_OF_ITERATIONS][2]; } time_stats_t; /* * Ambos g_time_stats e g_child_no sao variaveis globais, por que o * manipulador de sinais necessita dessas informacoes e nao ha como * passar dados atraves de sinais no System V */ time_stats_t *g_time_stats; int g_child_no; /* * Rotinas */ void SigHandler(int sig); void SigChild(void); /* * Inicio do Programa Principal */ int main( int argc, char *argv[] ) { /* * Variaveis necessarias */ int rtn; int count; float delta; float total = 0; float maximum = 0; /* * Variaveis relacionadas com memoria compartilhada */ int shm_id; key_t key = SHARED_MEMORY_ID; char *tmp_addr; /* * O estabelecimento da mascara de sinais */ sigset_t sigset; /* * Cria a memoria compartilhada */ if( (shm_id = shmget(key, sizeof(time_stats_t), IPC_CREAT | 0666)) == -1 ) { fprintf(stderr,"Impossivel criar a memoria compartilhada!\n"); exit(1); } /* * Associa ao segmento de memoria compartilhada */ if( (tmp_addr = (char *)shmat(shm_id, NULL, 0)) == (char *)-1 ) { fprintf(stderr,"Impossivel associar ao segmento de memoria compartilhada!\n"); exit(1); } /* * Estabelecimento do ponteiro g_time_stats */ g_time_stats = (time_stats_t *)tmp_addr; /* * Estabelece uma mascara de sinais default que boqueia SIGUSR1, * que e o sinal que ira ser enviado entre os filhos. Ambos os * filhos herdam essa mascara. */ sigemptyset( &sigset ); sigaddset( &sigset, SIGUSR1 ); sigprocmask( SIG_BLOCK, &sigset, NULL ); /* * Cria dois filhos * * NOTA: Lembrar que os filhos herdam a memoria compartilhada e * o estabelecimento de sinais! */ rtn = 1; for( count = 0; count < 2; count++ ) { if( rtn != 0 ) { rtn = fork(); } else { break; } } /* * Verifica se o valor retornado para determinar se e pai ou filho */ if( rtn == 0 ) { /* * Sou filho */ printf("Filho %i iniciado ...\n", count); /* * Estabelece o numero do filho e seu pid */ g_child_no = count; g_time_stats->child_pid[count - 1] = getpid(); /* * Inicia o filho */ SigChild(); exit(0); } else { /* * Dorme (finalmente nao precisa usar usleep !!!!) por um segundo * para permitir que os filhos iniciem e depois envia o primeiro * sinal para comecar o divertimento ! */ sleep(1); kill(g_time_stats->child_pid[0], SIGUSR1); /* * Agora aguardo que meus filhos terminem */ wait(NULL); wait(NULL); /* * Remove a memoria compartilhda */ if( shmctl(shm_id,IPC_RMID,NULL) != 0 ) { fprintf(stderr,"Impossivel remover a memoria compartilhada!\n"); exit(1); } /* * Calcula os tempos de demora */ for( count = 0; count < NO_OF_ITERATIONS - 1; count++ ) { delta = g_time_stats->timings[count][1].tv_sec - g_time_stats->timings[count][0].tv_sec; delta += (g_time_stats->timings[count][1].tv_usec - g_time_stats->timings[count][0].tv_usec)/(float)MICRO_PER_SECOND; total += delta; if( delta > maximum ) { maximum = delta; } delta = g_time_stats->timings[count+1][0].tv_sec - g_time_stats->timings[count][1].tv_sec; delta += (g_time_stats->timings[count+1][0].tv_usec - g_time_stats->timings[count][1].tv_usec)/(float)MICRO_PER_SECOND; total += delta; if( delta > maximum ) { maximum = delta; } } printf("A latencia media dos sinais foi: %.6f\n", total/(float)(NO_OF_ITERATIONS-1)/2); printf("A latencia maxima dos sinais foi: %.6f\n", maximum ); exit(0); } } /* * Este e o manipulador de sinais que sera chamado quando o filho receber * um sinal de seu irmao. Note o i++ embutido na indexacao da matriz, este * incrementara a localizacao de armazenamento cada vez que o manipulador * for ativado */ void SigHandler(int sig) { static int i = 0; gettimeofday( &g_time_stats->timings[i++][g_child_no % 2] , NULL ); kill( g_time_stats->child_pid[g_child_no % 2], SIGUSR1 ); } /* * Este e o filho que sera inciado pelo pai. Ele estabelecera o manipulador * de sinais e executara outras tarefas necessarias */ void SigChild(void) { /* * Variaveis locais */ int i; /* * Variaveis para sinais */ struct sigaction act; sigset_t suspend_set; /* * Cria um conjunto de sinais que desbloqueia SIGUSR1. Isto sera * instalado enquanto fica-se suspenso aguardando pelo sinal. */ sigfillset( &suspend_set ); sigdelset( &suspend_set, SIGUSR1 ); /* * Estabelecimento da estrutura sigaction e instalacao do SigHandler. */ act.sa_handler = &SigHandler; sigfillset( &act.sa_mask ); act.sa_flags = 0; if( sigaction( SIGUSR1, &act, NULL ) == -1 ) { fprintf(stderr, "Filho nao consegue estabelecer o manipulador de sinais!\n"); exit(1); } /* * Repita o numero especificado de vezes e aguarde pelo sinal */ for( i = 0; i < NO_OF_ITERATIONS; i++ ) { sigsuspend( &suspend_set ); } /* * Tudo terminado, o controle volta para o pai */ return; }