[Computação Paralela] – Thread

Computação paralela é uma forma de computação em que vários cálculos são realizados simultaneamente, operando sob o princípio de que grandes problemas geralmente podem ser divididos em problemas menores, que então são resolvidos concorrentemente (em paralelo).

  •  Thread

Thread é uma forma de um processo dividir a si mesmo em duas ou mais tarefas que podem ser executadas concorrentemente. 

O suporte à thread é fornecido pelo próprio sistema operacional (SO). Uma linha de execução permite que o usuário de programa, por exemplo, utilize uma funcionalidade do ambiente enquanto outras linhas de execução realizam outros cálculos e operações.

Em hardwares com apenas uma única CPU, cada linha de execução (Thread) é processada de forma aparentemente simultânea, pois a mudança entre uma linha e outra é feita de forma tão rápida que para o usuário isso está acontecendo paralelamente.

Em hardwares com multiplos CPUs ou multi-cores as linhas de execução (Threads) podem ser realizadas realmente de forma simultânea.

Os sistemas que suportam apenas uma única linha de execução são chamados de monothread e aqueles sistemas que suportam múltiplas linhas de execução são chamados de multithread.

exemplo_thread.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define THREADS_MAX 4

void *function(void *param)

{
     int id = (int)param;
     int i, loops = 10;

     for(i = 0; i < loops; i++)
     {
          printf("thread %d: loop %d\n", id, i);
     }
     pthread_exit(NULL);

}

int main(void){
     pthread_t threads[THREADS_MAX];
     int i;

     printf("pre-execution\n");
     for (i = 0; i < THREADS_MAX; i++)
     {
          pthread_create(&threads[i], NULL, function, (void *)i);
     }

     printf("mid-execution\n");

    for (i = 0; i < THREADS_MAX; i++)
     {
     pthread_join(threads[i], NULL);
     }

     printf("post-execution\n");

     return EXIT_SUCCESS;
}

Compile

fredim@novagenesis:~$ gcc -o thread exemplo_thread.c -pthread

Execute

fredim@novagenesis:~$ ./threadpre-execution

mid-execution

thread 0: loop 0

thread 0: loop 1

thread 0: loop 2

thread 0: loop 3

thread 0: loop 4

thread 0: loop 5

thread 0: loop 6

thread 0: loop 7

thread 0: loop 8

thread 0: loop 9

thread 1: loop 0

thread 1: loop 1

thread 1: loop 2

thread 1: loop 3

thread 1: loop 4

thread 1: loop 5

thread 1: loop 6

thread 1: loop 7

thread 1: loop 8

thread 1: loop 9

thread 3: loop 0

thread 3: loop 1

thread 3: loop 2

thread 3: loop 3

thread 3: loop 4

thread 3: loop 5

thread 3: loop 6

thread 3: loop 7

thread 3: loop 8

thread 3: loop 9

thread 2: loop 0

thread 2: loop 1

thread 2: loop 2

thread 2: loop 3

thread 2: loop 4

thread 2: loop 5

thread 2: loop 6

thread 2: loop 7

thread 2: loop 8

thread 2: loop 9

post-execution

[Compiladores] – Makefile

make é uma ferramenta muito importante quando se faz um projeto de programação. Sua finalidade é diminuir o tempo de compilação quando já se compilou o programa pelo menos uma vez. Isto é feito compilando-se apenas o que foi alterado, sem precisar recompilar o programa todo de novo.

O arquivo que o make usa para saber o que compilar e como, é o Makefile.

 Estrutura de um arquivo Makefile:

[CONSTANTES]
all: [DEPENDÊNCIAS]
[TAB] [COMANDO PARA COMPILAR]
[SEÇÃO]: [DEPENDÊNCIA] [TAB] [COMANDO PARA COMPILAR]

Exemplo: Usando Makefile

printmsg.h

#include <stdio.h>

int printmsg() {
    printf("Olá, Mundo!\n");

    return 0;

}

principal.c

#include <stdio.h>

#include "printmsg.h"

void main() {

     printmsg();

}

Makefile

all: printmsg.h

     gcc -o principal principal.c

Executando no Terminal Linux:

Compile

frederico@novagenesis:~$ make

gcc -o principal principal.c

Execute

fredericom@novagenesis:~$ ./principal

Olá, Mundo!

[Compiladores] – Como funciona o processo de compilação de um código-fonte usando GCC

Quando o GCC é invocado, ele normalmente realiza quatro etapas para gerar o executável: Pré-Processamento, Compilação, Montagem e Linkagem (Ligação), sempre nesta ordem e sendo que é possível parar o processo no final de cada etapa.

1 – Pré-Processamento

Esta etapa é responsável pela resolução de diretrizes do pré-processador, como #define, #if, #include

fredericom@novagenesis:~$ gcc -E teste.c -o teste.i

* Este comando redireciona a saída do pré processador para o arquivo teste.i

2 – Compilação

Nesta fase é produzida a linguagem de montagem dos arquivos de entrada.

fredericom@novagenesis:~$ gcc -S teste.c

*O arquivo teste.s será gerado, já em linguagem assembly da arquitetura.

3 – Montagem

Produz o arquivo objeto .o, levando em conta a linguagem de montagem dos arquivos de entrada.

fredericom@novagenesis:~$ gcc -c teste.c

*Gera o arquivo objeto teste.o

4 – Linkagem

Nesta fase os arquivos .o e as bibliotecas são “colocados” no executável.

fredericom@novagenesis:~$ gcc -o teste teste.c

* Gera o arquivo executável teste

No entanto, a maioria dos programas consistem em vários arquivos de código-fonte. Caso hajam dois arquivos, arquivo1.c e arquivo2.c, a seguinte linha de comando poderia ser utilizada para compilar o programa final:

fredericom@novagenesis:~$ gcc -o teste arquivo1.c arquivo2.c

O GCC interpreta os arquivos de acordo com a sua extensão. Algumas das principais podem ser vistas na tabela abaixo:

Extensão

Interpretação

.c

Programa e linguagem C

.C .cc

Programa em linguagem C++

.i

Programa em C pré-processado

.ii

Programa em C++ pré-processado

.S .s

Programa em linguagem Assembly

.o

Programa objeto

.a .so

Bibliotecas compiladas

[Solved] – I2CImp.lo is not a valid libtool object

libtool: link: `/home/fredim/Downloads/librxtx-java/rxtx-2.2pre2/i686-pc-linux-gnu/I2CImp.lo’ is not a valid libtool object
make: ** [i686-pc-linux-gnu/librxtxI2C.la] Erro 1

Solução:

sudo nano /usr/include/linux/version.h

Adicionar o resultado do uname -r

uname -r
3.2.0-23-generic-pae //Resultado do uname -r

Ficando:

#define UTS_RELEASE “3.2.0-23-generic-pae”