_Isis_ 202 Denunciar post Postado Dezembro 12, 2008 Trabalho da matéria de Sistemas Operacionais desse semestre. O programa deveria aceitar um diretório onde comparar arquivos e unificar a árvore usando hardlinks e threads. Como otimização não era bônus, usei um algoritmo O(N2). Além do diretório, o programa aceita um argumento -np que determina o número total de threads e o argumento -v, que determina a impressão dos arquivos unificados no final da execução. GCC 4.3.1 Pthreads comum.h #include <stdio.h> #include <stdbool.h> #include <stdlib.h> checarArgumentos.h #include "comum.h" #include <ctype.h> #include <string.h> #define OP_NUM "Uso: unifica [-v] [-np <numero_de_threads>] diretório\n" bool digitos(char *); void checarArgumentos(int, char*[]); constroiLista.h #include <ftw.h> #include <string.h> #include "comum.h" #include "tipoArquivos.h" int adicionaNome(const char *, const struct stat *, int); funcoesThreads.h #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define ERR_ALOC "Erro na alocação de memória na thread principal.\n" typedef struct { unsigned int tamanhoArquivo; // Chave da lista. unsigned int tamanhoLista; // Tamanho da lista dos nomes de arquivos unificados. char ** caminho; // Lista dos arquivos de tamanho correspondente a <i>tamanhoArquivo</i> que foram unificados. } Unificados; // Utilizada para passar os nomes dos arquivos para as threads comparadoras. typedef struct { char * arquivo1; char * arquivo2; } dadosThread; int buscaTamanho(int); void *thread_principal(); void *thread_comparadora(void *); char *repetido(const char *,const Unificados ); void libera(); tipoArquivos.h #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> bool tiposIguais(struct stat* , struct stat * ); bool tamanhosIguais(struct stat* , struct stat* ); bool thread_diff(const char *, const char *); bool diffPreparacao(const char *, const char*); checarArgumentos.c #include "checarArgumentos.h" extern bool isVerbose; extern int threadQtd; /** @brief Verifica se o argumento passado para <i>-np</i> contém apenas dígitos. * @param Argumento da opção <i>-np</i>. * @return <b>True</b> se o argumento contém apenas dígitos e <b>False</b> caso contrário. */ bool digitos(char *argumento) { int tamanho = strlen(argumento); for(int indice = 0; indice < tamanho; indice++) if (!isdigit(argumento[indice])) return false; return true; } /** @brief Verifica se os argumentos estão corretos. * @param <i>Numero</i>: número de argumentos da linha de comando. * @param <i>Args</i> : array de strings contendo os argumentos. * @note Os argumentos devem ser digitados em uma ordem fixada (a linha de comando não é fl exivel). */ void checarArgumentos(int numero, char *args[]) { if ( numero < 2 || numero > 5) { fprintf(stderr,OP_NUM); exit (1); } // Caso 1: O usuário fornece apenas 2 argumentos, sendo um deles uma das opções if (numero == 2 && (!strcmp("-v",args[1]) || !strcmp("-np",args[1]))) { fprintf(stderr,OP_NUM); exit(1); } /* Caso 2: com 3 argumentos, temos: * unifica -v diretorio (válido, mesmo que diretório seja um número ou comece com um da sh) * unifica -v -np (inválido) * unifica -np 3 (inválido) * unifica -np diretorio (inválido) */ if (numero == 3) { if (!strcmp(args[1],"-v")) { if (!strcmp(args[2],"-np")) { fprintf(stderr,OP_NUM); exit(1); } isVerbose = true; } else if (!strcmp(args[1],"-np")) { fprintf(stderr,OP_NUM); exit(1); } } /* Caso 3: com 4 argumentos, temos as seguintes possibilidades: * unifica -np 3 diretorio (valido) * As demais são inválidas (unifica -v -np 3 , unifica -v 2 -np, unifica -v -np diretori o) */ if (numero == 4) { if (!strcmp(args[1],"-np") && digitos(args[2]) && (strcmp(args[3],"-v") || strcmp(arg s[3],"-np"))) threadQtd = atoi(args[2]); else { fprintf(stderr,OP_NUM); exit(1); } } if (numero == 5) { if (!strcmp(args[1],"-v") && !strcmp(args[2],"-np") && digitos(args[3])) { isVerbose = true; threadQtd = atoi(args[3]); } else { fprintf(stderr,OP_NUM); exit(1); } } } confereArquivos.h #include "comum.h" #include "tipoArquivos.h" #include <string.h> #define EOPEN 1 #define CHUNK 512 #define ER_FOPEN "Erro ao abrir arquivo.\n" char errors = 0; /** @brief Verifica se os arquivos são do mesmo tipo. * @param Estrutura contendo as informações do arquivo #1. * @param Estrutura contendo as informações do arquivo #2. * @return <i>True</i> se os arquivos forem do mesmo tipo, <i>False</i> caso contrário. * @note São testados somente arquivos regulares (os hardlinks se encaixam nesse grupo). */ bool tiposIguais(struct stat* info1, struct stat * info2) { return (S_ISREG(info1->st_mode) == S_ISREG(info2->st_mode)); } /** @brief Verifica se os dois arquivos possuem tamanhos iguais. * @param Estrutura contendo as informações do arquivo #1. * @param Estrutura contendo as informações do arquivo #2. * @return <i>True</i> se os arquivos possuírem o mesmo tamanho. <i>False</i> caso contrário . */ bool tamanhosIguais(struct stat* info1, struct stat* info2) { return (info1->st_size == info2->st_size); } /** @brief Realiza a comparação entre dois arquivos através da leitura de seu conteúdo. * @param Caminho do arquivo #1. * @param Caminho do arquivo #2. * @return <i>True</i> se os arquivos são iguais. <i>False</i> caso contrário. */ bool thread_diff(const char *fp1, const char *fp2) { FILE *arquivo_1 = fopen(fp1, "rb"); FILE *arquivo_2 = fopen(fp2, "rb"); if (arquivo_1 == NULL || arquivo_2 == NULL) { perror(ER_FOPEN); exit (1); } size_t lidos; char *bufferArq1 = (char*)malloc(sizeof(char)*CHUNK); char *bufferArq2 = (char*)malloc(sizeof(char)*CHUNK); if (bufferArq1 != NULL && bufferArq1 != NULL) { int idx; /* Lê uma quantidade de bytes especificados em CHUNK nos dois arquivos * e compara um a um. Ao encontrar o primeiro byte diferente, fecha ambos os arquivos e retorna false. * Senão, continua lendo e comparando até chegar ao final do arquivo ou encontrar um byte diferente. */ while (!feof(arquivo_1) && !feof(arquivo_2)) { lidos = fread(bufferArq1,sizeof(char),CHUNK,arquivo_1); fread(bufferArq2, sizeof(char), CHUNK, arquivo_2); if (lidos > 0) { idx = 0; while (idx < lidos && bufferArq1[idx] == bufferArq2[idx]) idx++; if (idx < lidos) { fclose(arquivo_1); fclose(arquivo_2); return false; } } } fclose(arquivo_1); fclose(arquivo_2); return true; } } /** @brief Realiza a comparação entre os arquivos especificados como argumentos. * @param Caminho do arquivo #1. * @param Caminho do arquivo #2. * @return <i>True</i> Se os arquivos forem iguais, <i>False</i> caso contrário. * @detail Existe uma seqüência de operações a serem realizadas. Primeiro se compara os tipos dos arquivos, para evitar a comparação de um link simbólico com um arquivo regular, por exemplo.<br/> * Se os dois arquivos forem do mesmo tipo, verifica-se seu temanho. Apenas se os tamanhos forem iguais a verificação através da leitura do conteúdo é feita. */ bool diffPreparacao(const char *fp1, const char*fp2) { struct stat infoArq1, infoArq2; // PROBLEMA DO FSTAT COM UM HARDLINK JÁ EXISTENTE. RETORNA -1 E ENOENT (errno) if (stat(fp1,&infoArq1) == 0 && stat(fp2,&infoArq2) == 0) { if (tiposIguais(&infoArq1,&infoArq2) ){ if (infoArq1.st_size != infoArq2.st_size) return false; //TODO condicao para arquivos de tamanho maior que zero return thread_diff(fp1, fp2); } else return false; } else { fprintf(stderr,"%m (%d) -- %d\n",errno,ENOENT); exit (1); } } constroiLista.c #include "constroiLista.h" int arquivosExistentes = 0; char ** lista; /** @brief Monta a lista dos arquivos do diretório a ser unificado. * @param Caminho do arquivo lido por <i>ntfw</i>. * @param Estrutura com os dados do arquivo obtidos via <i>ntfw</i>, provavelmente por <i>stat</i>. * @param <i>Não é usado</i>. * @param <i>Não é usado</i>. * @note Chamado pela função <i>ntfw</i>. */ int adicionaNome(const char *fpath, const struct stat *sb, int tflag) { if (S_ISREG(sb->st_mode)) { lista = (char**) realloc(lista,sizeof(char*)*(arquivosExistentes+1)); lista[arquivosExistentes] = (char*)malloc((strlen(fpath)+1)*sizeof(char)); strcpy(lista[arquivosExistentes],fpath); arquivosExistentes++; } return 0; } funcoesThreads.c #include "funcoesThreads.h" #include <stdbool.h> #include <string.h> Unificados * listaUnificados; unsigned int tamanhoUnificados; extern bool isVerbose; extern int arquivosExistentes; extern int threadQtd; extern char ** lista; extern bool diffPreparacao(const char *fp1, const char*fp2); pthread_mutex_t mutex1, mutex2, mutex3,mutex4; /** @brief Procura na lista dos arquivos unificados por um registro relacionado ao tamanho de arquivo * passado como argumento. * @param Tamanho do arquivo a ser procurado. * @return -1 se o tamanho não for encontrado e, caso contrário, sua posição na lista. */ int buscaTamanho(int tamanhoDoArquivo) { for(int i=0; i<tamanhoUnificados; i++) { if (listaUnificados[i].tamanhoArquivo == tamanhoDoArquivo) return i; } return -1; } /** @brief Realiza uma busca sequencial na lista de arquivos unificados para evitar nomes * repetidos. * @param Arquivo a ser procurado. * @param Estrutura que possivelmente contém o nome do arquivo pesquisado. * @return <i>NULL</i> se a chave não foi encontrada. Retorna o endereço dela na lista cont ida na * estrutura caso contrário. */ char * repetido(const char * chave, const Unificados celula) { for(int i=0; i< celula.tamanhoLista; i++) if (!strcmp(chave, celula.caminho[i])) return celula.caminho[i]; return NULL; } /** @brief Chama as funções em <i>confereArquivos.c</i>. * @details Caso seja feito algum hardlink, adiciona um elemento a uma lista de estruturas, * que contém o tamanho do arquivo unificado e uma lista de nomes dos links criados. */ void *thread_comparadora(void * compartilhamento) { char * arq2; char * arq1; int posicao; struct stat st; arq2 = (char*) malloc(0); arq1 = (char*) malloc(0); while(1) { pthread_mutex_lock(&mutex1); arq2 = (char*)realloc(arq2, (strlen(((dadosThread*)compartilhamento)->arquivo2)+1)*si zeof(char)); strcpy(arq2, ((dadosThread*)compartilhamento)->arquivo2); arq1 = (char*) realloc(arq1, (strlen(((dadosThread *)compartilhamento)->arquivo1)+1)* sizeof(char)); strcpy(arq1, ((dadosThread*)compartilhamento)->arquivo1); // Assinala que a thread principal pode modificar os índices. pthread_mutex_unlock(&mutex2); if (diffPreparacao(arq1, arq2)) { pthread_mutex_lock(&mutex3); /* Para criar o hardlink é necessário remover o arquivo que se tornará o link, caso contrário, * a função indica o erro em errno. */ remove(arq2); link(arq1, arq2); if (isVerbose) { stat(arq1, &st); posicao = buscaTamanho(st.st_size); /* Se o tamanho já existe na lista de unificados, simplesmente adiciona o caminho do arquivo na * lista interna. */ if (posicao > -1) { if ( repetido(arq1,listaUnificados[posicao]) == NULL ) { listaUnificados[posicao].caminho = (char**)realloc(listaUnificados[posicao].caminho, (listaUnificados[posicao].tamanhoLista+1)*sizeof(char*)); listaUnificados[posicao].caminho[listaUnificados[posicao].tamanhoLista] = (char*)malloc(strlen(arq1)); strcpy(listaUnificados[posicao].caminho[listaUnificados[posicao].tamanhoLista], arq1); listaUnificados[posicao].tamanhoLista++; } } else { listaUnificados = (Unificados*)realloc(listaUnificados,(tamanhoUnificados+1)*sizeof(Unificados)); listaUnificados[tamanhoUnificados].tamanhoArquivo = st.st_size; listaUnificados[tamanhoUnificados].caminho = (char**)malloc(1*sizeof(char *)); listaUnificados[tamanhoUnificados].caminho[0] = (char*) malloc((strlen(arq1)+1)*sizeof(char)); strcpy(listaUnificados[tamanhoUnificados].caminho[0], arq1); listaUnificados[tamanhoUnificados].tamanhoLista = 1; tamanhoUnificados++; } } pthread_mutex_unlock(&mutex3); } //FIM DO IF DE DIFF pthread_mutex_unlock(&mutex4); } //FIM DO WHILE free(arq1); free(arq2); arq1 = arq2 = NULL; } /** @brief Imprime a lista dos arquivos unificados (os que foram transformados em hardlinks). */ void imprimeUnificados() { int idxTemp; while (tamanhoUnificados > 0) { puts("---------------------------------------------------------------------"); printf("Arquivos unificados de tamanho %d\n",listaUnificados[tamanhoUnificados-1].tamanhoArquivo); idxTemp = listaUnificados[tamanhoUnificados-1].tamanhoLista; while ( idxTemp > 0) { printf("\t%s\n",listaUnificados[tamanhoUnificados-1].caminho[idxTemp-1]); idxTemp--; } tamanhoUnificados--; } } /** @brief Thread que cria o pool e distribui o trabalho. */ void *thread_principal() { dadosThread compartilhamento; compartilhamento.arquivo1 = (char*)malloc(0); compartilhamento.arquivo2 = (char*)malloc(0); if(compartilhamento.arquivo1 == NULL || compartilhamento.arquivo2 == NULL) { fprintf(stderr,ERR_ALOC); pthread_exit(NULL); } // Inicializar lista de arquivos unificados. listaUnificados = (Unificados*) malloc(0); pthread_t listaThreads[threadQtd-1]; int indice; unsigned int idxFixo = 0; unsigned int idxVar = 1; tamanhoUnificados = 0; // Código de tratamento de erro omitido. pthread_mutex_init(&mutex1, 0); pthread_mutex_lock(&mutex1); pthread_mutex_init(&mutex2, 0); pthread_mutex_lock(&mutex2); pthread_mutex_init(&mutex4,0); pthread_mutex_lock(&mutex4); // O mutex3 deve ser destrancado. pthread_mutex_init(&mutex3, 0); for(indice = 0; indice < threadQtd-1; indice++) pthread_create(&listaThreads[indice], NULL, thread_comparadora, &compartilhamento); while(1) { compartilhamento.arquivo1 = (char*)realloc(compartilhamento.arquivo1, (strlen(lista[idxFixo])+1)*sizeof(char)); strcpy(compartilhamento.arquivo1, lista[idxFixo]); compartilhamento.arquivo2 = (char*)realloc(compartilhamento.arquivo2, (strlen(lista[i dxVar])+1)*sizeof(char)); strcpy(compartilhamento.arquivo2, lista[idxVar]); // Sinalizar que uma thread comparadora pode ler a estrutura. pthread_mutex_unlock(&mutex1); // Esperar sinal da thread comparadora para incrementar os índices pthread_mutex_lock(&mutex2); if (idxVar < arquivosExistentes) idxVar++; if (idxVar == arquivosExistentes) { idxFixo++; idxVar = idxFixo+1; } pthread_mutex_lock(&mutex4); /* O problema aqui ocorre se a thread principal for alocada para executar antes que a última thread * comparadora coloque o resultado do diff no array de unificados. Nesse caso, quando -v é * especificado, o resumo da unificacao não é impresso. * Por isso o mutex no início dessa regiao: ele é iniciado trancado pela thread principal antes das * threads comparadoras serem criadas. */ if (idxVar == arquivosExistentes && idxFixo == arquivosExistentes-1) break; }// FIM DO WHILE /** * Se for modo verbose, ao terminar, imprime o conteúdo da lista criada pelas threads comparadoras. */ if (isVerbose) imprimeUnificados(); free(listaUnificados); listaUnificados = NULL; pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); pthread_mutex_destroy(&mutex3); pthread_mutex_destroy(&mutex4); pthread_exit(NULL); } unifica.c #include "checarArgumentos.h" #include "constroiLista.h" #include "funcoesThreads.h" #include "tipoArquivos.h" #define MAX_THREADS 32 #define ERR_THR "Número de threads maior do que o permitido (%d).\n" bool isVerbose = false; int threadQtd = 2; extern int arquivosExistentes; extern char **lista; void libera() { for(int i=0;i<arquivosExistentes;i++) { free(lista[i]); lista[i] = NULL; } } int main(int argc, char **argv) { // O diretório deve ser o último argumento oferecido. checarArgumentos(argc,argv); // Verificar limite de threads. if (threadQtd > MAX_THREADS) { fprintf(stderr,ERR_THR,MAX_THREADS); exit (1); } // Verificar se diretório existe struct stat temp; if (stat(argv[argc-1],&temp) == -1) { fprintf(stderr,"%m\n"); exit(1); } // Criar lista dos arquivos. lista = (char**)malloc(0); ftw(argv[argc-1], &adicionaNome,20); // Criar thread distribuidora. pthread_t distribuidora; pthread_create(&distribuidora, NULL, thread_principal, NULL); int threadError; if ( (threadError = pthread_join(distribuidora, NULL)) ) { fprintf(stderr,"%m - (%d)",threadError); free(lista); lista = NULL; exit(1); } free(lista); lista = NULL; return 0; } Compartilhar este post Link para o post Compartilhar em outros sites
Edultra 13 Denunciar post Postado Dezembro 12, 2008 A Isis sempre mandando bem, ade um dia eu chegar neste nivel. Parabens! Compartilhar este post Link para o post Compartilhar em outros sites