2009-05-02

Processos, parte I

Background

O objectivo do exercício 3.1 é implementar um programa que inicie vários processos para procurar um número numa matriz. Cada um dos processos deve procurar numa das linhas da matriz. Para isso vamos usar a chamada ao sistema fork.

Consultando a página do manual do fork podemos ver que esta função cria uma cópia do processo actual. Esta cópia é praticamente igual em tudo ao nosso processo original. É exactamente o mesmo programa, tem exactamente o mesmo conteúdo na memória e até está a executar no mesmo ponto do código.

Criar um processo com fork é uma operação bastante leve. Isto porque é usada uma técnica chamada copy-on-write para efectuar esta cópia. Copy-on-write significa que quando invocamos fork, nada é copiado. O novo processo irá usar a mesma memória que o processo original estava a usar. Apenas quando um dos dois alterar uma parte da memória é que esta vai ser copiada para o novo.

Geralmente chama-se pai ao processo que invoca fork e filho ao processo que é criado pelo fork. No pai fork devolve o PID (process ID) do novo processo que foi criado. No filho devolve 0. É esta diferenciação, que nos permite correr código diferente no pai e no filho, com uma estrutura semelhante a esta:

pid_t pid = fork();
if(pid == 0)
{
// filho
}
else
{
// pai
}

Resolução

Esta vai ser a nossa matriz:

#define LINES 20
#define COLUMNS 100000

int matrix[LINES][COLUMNS];

Vamos então começar por escrever uma função que procura por um número numa linha e imprime esse número assim que o encontrar.

void find(int* line, int lineNr, int x)
{
int i;
for(i = 0; i < COLUMNS; i++)
{
if(line[i] == x)
printf("%d found at line %d, column %d.\n", x, lineNr, i);
}
}

Bastante simples. Agora vamos criar um processo para cada linha. Desta forma podemos procurar em várias linhas ao mesmo tempo. Para isso vamos escrever um ciclo com a utilização típica do fork:

   int i;
for(i = 0; i < LINES; i++)
{
pid_t pid = fork();
if(pid == 0)
{
find(matrix[i], i, x);
exit(0);
}
}

Como o pai não tem nenhum código extra para correr, não existe else. Reparem também na utilização de exit. Se não tivessemos usado exit, cada processo filho iria procurar na respectiva linha, e depois continuava o ciclo, lançando mais processos. Esses novos processos iriam repetir esse erro e lançar ainda mais processos. No final teríamos perto de 20^20 processos!

Usando exit terminámos os processos-filho e podemos devolver um código de saída para indicar erros.

Agora só falta obtermos o número x, que é passado como argumento:

int main(int argc, char** argv)
{
int x = atoi(argv[1]);

E inicializar a matriz aleatóriamente:

    for(i = 0; i < LINES; i++)
for(j = 0; j < COLUMNS; j++)
{
matriz[i][j] = rand() % 10;
}

Se correrem este código podem ver as várias ocorrências de um determinado número serem impressas. Reparem que as ocorrências são impressas fora de ordem. Isto deve-se ao facto de serem vários processos a procurar. No segundo exercício iremos forçar ordem nestes resultados.

Notas finais

Este exercício era pequeno e bastante simples. O segundo exercício adiciona um factor extra de confusão, mas não é nada do outro mundo.

4 commentários:

Anónimo disse...

Consegui resolver o segundo exercício caso queiras, posso-te enviar o código, fazes a tua análise e podes já avançar para o próximo guião caso pretendas ou te der jeito.

Diz alguma coisa.

Martinho Fernandes disse...

Podes enviar para noreplyforsure@gmail.com. Dá-me mesmo muito jeito. Assim posso adiantar o guião do exec, porque tenho de rever umas cenas que já não me lembro muito bem.

Miguel Andrade disse...

Apenas uma correcção.
Passar disto:

for(i = 0; i < LINES; i++)
for(j = 0; i < COLUMNS; i++)
{
matriz[i][j] = rand() % 10;
}

para isto:
for(i = 0; i < LINES; i++)
for(j = 0; j < COLUMNS; j++)
{
matriz[i][j] = rand() % 10;
}

Por razões óbvias. :)
Acontece.
Parabéns, mais uma vez pelo teu excelente blog.

Martinho Fernandes disse...

@Miguel: Obrigado pelo reparo. Apesar de ser tarde, já corrigi.

Enviar um comentário