Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

_Isis_

[Código] Diff de arquivos com threads

Recommended Posts

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

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.