Professional Documents
Culture Documents
SISTEMAS OPERATIVOS I
Maio de 2006
Agradecimentos
Agrademos aos colegas Jorge Pinto Leite e Antnio Costa a sua participao na reviso deste
documento.
NDICE
Agradecimentos ......................................................................................................................................2
NDICE ...................................................................................................................................................2
1 Introduo .......................................................................................................................................2
2 Tpicos sobre programao em Linux ................................................................................................2
2.1
Codificao de um programa ......................................................................................................2
Formatao do cdigo.......................................................................................................................3
Comentrios .....................................................................................................................................4
Nomes das Variveis e funes..........................................................................................................5
2.2
Gesto de erros em Linux ...........................................................................................................6
2.3
Outras funes relacionadas com a gesto de processos ..............................................................6
3 Processos em Linux ..........................................................................................................................7
3.1
Estados de um processo .............................................................................................................7
3.2
Representao de um processo...................................................................................................8
4 Programao multitarefa em Linux....................................................................................................8
4.1
Criao de um processo fork()..............................................................................................8
4.2
Funo exit() ....................................................................................................................... 11
4.3
Funes wait() e waitpid()................................................................................................ 11
5 Bibliografia ..................................................................................................................................... 14
1 Introduo
Estes apontamentos tem como objectivo dar a conhecer aos alunos de Sistemas operativos I alguns
conceitos bsicos sobre o modo de programao do Sistema Operativo (SO) Linux assim como
sobre os mecanismos de programao multitarefa.
Pgina 2 de 14
O estilo de programao inclui vrio temas: formatao do cdigo, nomes das variveis e funes,
convenes relativas interface das funes, escrita de rotinas, etc. De seguida so indicados alguns
concelhos e exemplos (para um tratamento mais completo deste tema aconselhada a consulta da
bibliografia).
Formatao do cdigo
Os objectivos de uma boa formatao do cdigo so:
1. Representar com preciso a estrutura do cdigo
2. Melhorar a compreenso do cdigo
3. Facilitar modificaes do cdigo
Tcnicas de formatao:
1. Utilizao de espaos: a utilizao de espaos, tabs e linhas em branco permite organizar um
programa e melhorar a sua aparncia. O exemplo seguinte mostra como se podem separar
duas variveis num if:
#include<stdio.h>
main()
{
if (a == b)
{
4. Identao: permite mostrar a estrutura lgica de um programa. Como regra devem ser
identadas todas as linhas de cdigo subordinadas mesma estrutura de controlo. O exemplo
seguinte mostra uma forma muito utilizada em C. Note-se que o fecho das estruturas de
controlo if devem estar alinhadas com o respectivo if e devem ser inseridos comentrios
no ponto de fecho.
if (a == b)
{
printf(abc\n);
if ((c > d) &&
(e > f) &&
(f < g))
{
printf(>>>>>\n);
} // if >
} // if a == b
Pgina 3 de 14
Comentrios
Um programa ou uma funo devem ter SEMPRE comentrios descrevendo o seu funcionamento.
No caso de um programa deve normalmente aparecer no incio as funcionalidades principais do
programa juntamente com os autores, (Figura 1). Como sugesto particular para a disciplina de
SOP1 aconselha-se a incluso no princpio do programa do texto do problema.
No caso de uma funo, o texto deve descrever os objectivos da funo, descrevendo em detalhe as
variveis de entrada e de sada da mesma, (Figura 2).
A insero de comentrios no meio do cdigo deve ser feita de forma criteriosa, utilizando uma
linguagem de alto nvel que descreva os objectivos de um pedao de cdigo, tal como
exemplificado no cdigo descrito na Figura 2 (note-se que para um programador experiente o
nmero de comentrios existentes neste programa pode ser considerado excessivo!).
/**********************************************************************
Nome do programa:
Autor:
Organizao:
Data:
Verso:
Exemplo
Joaquim Costa
ISEP
23/04/2005
1.00
main ()
sqr
Joaquim Costa
ISEP
26/04/2005
1.00
Parmetros:
double a:
valor a calcular, a deve ser maior ou igual a 0
Sada:
devolve um valor do tipo int com o resultado do factorial
Em caso de erro devolvido o valor -1
Esta funo permite calcular o factorial de um nmero.
*********************************************************************/
int fact (int a)
{
int f;
/* varivel contadora */
int res;
/* resultado */
/* Factorial de 1 igual a zero */
if (res == 1)
{
res = 0;
Introduo Programao Concorrente em Linux
Pgina 4 de 14
}
else
{
/* Calculo para os restantes casos
for(f = 1; f < a; f++)
{
res = res * f
}
}
/* devolve o resultado */
return(res);
} // fim sqr
Figura 2 Exemplo de comentrio a inserir no incio de uma funo
Nomes correctos
Velocidade,
velocidade,
VelocidadeEmKm
DataHoje, Data_de_Hoje
LinhasPorPagina
Nomes incorrectos
vel,
v,
vc,
x,
comboio
DH, hoje, data
LPP, linhas, x, x1
x1,
Conselhos teis:
O nome das variveis deve descrever o mais claramente possvel o seu propsito (mesmo
que para tal fique muito comprida!).
Adoptar sempre o mesmo tipo de conveno para os nomes. Pode-se usar sempre
maisculas para distinguir as palavras constituintes do nome da varivel (NomeDaVarivel)
ou ento o underscore (nome_da_variavel).
Dar nomes s variveis booleanas que impliquem verdadeiro ou falso, p.e.: done, erro.
Usar sempre nomes positivos para as variveis boolenas. Evitar utilizar nomes do tipo
NaoEncontrado
As funes devem seguir basicamente o mesmo tipo de convenes utilizadas nas variveis de uso
geral, i.e. devem reflectir o propsito da funo.
Pgina 5 de 14
A funo perror() permite imprimir a string que lhe passada atravs do apontador s, seguida
de : e de uma descrio do ltimo erro que ocorreu (p.e. File not found.). A string que passada
como argumento pode ser usada pelo programador, por exemplo, para indicar em que funo que
ocorreu o erro.
O exemplo apresentado na Figura 3, mostra forma como a funo perror() pode ser utilizada.
Neste caso, dado que o ficheiro x.txt foi aberto apenas para leitura a funo perror() na linha 13
indica que o descritor do ficheiro (varivel fp) est incorrecto, mensagem Bad file descriptor, dado
que o programa no tem permisses de leitura sobre o ficheiro aberto. Note-se que neste pequenos
programas de exemplo no so colocados comentrios uma vez que estes so descritos no texto e de
modo a poupar espao.
#include <stdio.h>
#include <stdlib.h>
main ()
{
FILE *fp;
char buffer[20];
int error;
fp = fopen("x.txt", "w");
if (fp == NULL)
{
perror("Erro na em fopen ");
exit(-1);
}
error = fscanf(fp,"%s", buffer);
if (error == -1)
{
perror("Erro na linha 19 ");
exit(-1);
}
}
Figura 3 Exemplo de utilizao da funo perror()
Pgina 6 de 14
A funo sleep permite que um processo adormea durante t segundos ou at que o processo receba
um sinal, i.e. o processo passa para o estado de Waiting. Retorna zero caso tenha adormecido
durante a totalidade dos t segundos ou os segundos que faltam para o seu trmino normal, caso a
sua execuo tenha sido interrompida por um sinal.
#include <unistd.h>
Unsigned int sleep(unsigned int t);
A funo getpid() permite devolver o PID do prprio processo e getppid() permite obter o
PID do processo pai.
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
3 Processos em Linux
Um programa uma entidade inactiva e esttica, constituda por um conjunto de instrues e
respectivos dados. Normalmente, num Sistema Operativo (SO), um programa pode existir sob a
forma de ficheiro em dois formatos bsicos, cdigo fonte ou cdigo executvel. Quando chamado,
lido para memria e executado sob a forma de um processo.
Um processo portanto uma instncia de um programa em execuo. Ao chamar um programa este
cria inicialmente apenas um processo. Posteriormente este pode criar outros processos cooperantes
com o processo inicial. Os SOs modernos permitem a execuo concorrente de mltiplos processos
(normalmente referida como capacidade de multitarefa).
Stoped
New
Terminated
Ready
to Run
Running
Waiting
Figura 4 Estados de um processo
Introduo Programao Concorrente em Linux
Pgina 7 de 14
No estado New o processo est a ser criado, passando de seguida para o estado Ready to Run. Neste
estado um processo espera at que o escalonador do SO liberte o(s) processador(es) e o coloque em
execuo (estado de Running). Um processo ento executado por um determinado espao de
tempo, de acordo com as polticas de escalonamento do SO. Pode deste estado passar para Waiting,
na existncia de qualquer situao de bloqueio, por exemplo caso o processo esteja espera de um
semforo. Pode tambm regressar ao estado Ready to run por deciso do escalonador. Finalmente,
quando termina a sua execuo o processo passa para o estado de Terminated, caso tenha terminado
correctamente. Se deixar alguma informao pendente (p. e. se a funo exit() tiver retornado
algum valor) ento o processo passa para o estado de Zombie. Um processo passa ao estado Stoped
quando recebe uma indicao para suspender a sua execuo, p.e. atravs de um sinal ou do
comando sleep().
Note-se que, autores diferentes, descrevem os estados de um processo de forma diferente, por isso
necessrio estar atento forma como o SO implementa estes estados.
com base nesta informao que o ncleo do SO vai gerir o evoluir de cada processo. O utilizador
tem sua disposio um conjunto de funes adequadas programao multitarefa, incluindo a
criao e gesto de processos, comunicao e sincronizao entre processos, etc. Na seco seguinte
iremos descrever algumas das funes do LINUX disponveis para a criao e gesto (bsica) de
um processo.
Pgina 8 de 14
Esta funo retorna -1 em caso de erro e atribui varivel errno (varivel global de controlo de
erros) o erro que ocorreu. Um forma de obter a descrio do erro que ocorreu recorrer funo
perror(). Caso a execuo da funo decorra sem erros, ao processo filho retornado o valor 0 e
ao processo pai retornado o PID do filho que acabou de ser criado.
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
A cpia criada quase igual ao processo original diferindo apenas em alguns detalhes. As seguintes
propriedades so herdadas pelo filho:
Directoria de trabalho;
Variveis de ambiente;
Limites de recursos:
etc.
etc.
Pgina 9 de 14
{
printf("Pai: Eu sou o processo Pai\n");
}
else /* Cdigo do FILHO */
{
printf("Filho:Eu sou o processo Filho\n");
}
} /* fim main */
Figura 5 Utilizao da funo fork()
O exemplo da Figura 6, demonstra de que forma pode ser feito o controlo de erros do programa
assim como a herana das variveis actuais pelo filho. Verifica-se, durante a execuo do programa,
que as alteraes ao valor das variveis feita por cada um dos processos no se vai reflectir no
outro.
#include
#include
#include
#include
<stdio.h>
<unistd.h>
<sys/types.h>
<stdlib.h>
main(void)
{
pid_t pid;
int a;
a = 1;
pid = fork(); /* Cria um PROCESSO */
if (pid < 0)
{
perror("Erro ao criar o processo:");
exit(-1);
}
else
{
if (pid >0) /* Cdigo do PAI */
{
printf("Pai: Eu sou o PAI\n");
printf("Pai: a=%d\n", a);
a = a + 1;
printf("Pai:a+1=%d\n", a);
}
else /* Cdigo do FILHO */
{
printf("Filho:Processo Filho\n");
printf("Filho:a=%d\n", a);
a = a + 1000;
printf("Filho:a+1000=%d\n", a);
}
}
exit(0);
} /* fim main */
Figura 6 Actualizao das variveis
Note-se que nos programas das Figuras 5 e 6 as funes printf() tem imprimem sempre se Pai: ou
Filho: no caso da impresso ser feita pelo processo pai ou pelo processo filho, respectivamente. Sugere-se
Introduo Programao Concorrente em Linux
Pgina 10 de 14
a utilizao desta norma em todos os programas de modo a facilitar a anlise dos resultados impressos no
ecr e permitir um debugging mais fcil dos programas.
A varivel status um inteiro e permite retornar apenas os seus 8 bits menos significativos para
o pai. Na seco seguinte ser descrito como que o processo pai pode obter o valor de status.
A funo wait() espera at que qualquer filho termine. waitpid() espera at que um processo
filho especfico termine, alm disso, esta funo tambm permite esperar por um filho sem bloquear
o processo pai. Os prottipos destas funes so apresentados a seguir.
1
Pgina 11 de 14
#include <sys/types.h>
#include <sys/wait.h>
pit_t wait(int *status);
pit_t waitpid(pit_t pid, int *status, int options)
status: um apontador para um inteiro. Se este apontador no for passado como nulo, ento
poder armazenar o estado de finalizao do processo filho. Note-se que este ponteiro j deve ter o
seu espao de memria reservado antes da evocao da funo.
pid: processo pelo qual a funo waitpid() vai esperar. Se pid igual a -1, waitpid() espera
por qualquer processo filho, pid > 0 ento waitpid() espera por um processo cujo pid seja igual
a pid.
options: 0 de modo a que o processo fique bloqueado espera do processo filho; pode tambm
ser igual ao OR (ou lgico) de duas constantes: WNOHANG, WUNTRACED. A primeira
constante significa que a funo retorna imediatamente se nenhum filho tiver terminado e a segunda
retorna tambm o estado dos filhos que se encontrem no estado de Stopped (opo pouco utilizada).
Ambas as funes wait() e waitpid() retornam o PID do processo que terminou em caso de
sucesso e -1 em caso de erro.
Waitpid() retorna 0 caso nenhum filho tenha terminado e se a opo WNOHANG tivesse sido
usada na chamada da funo.
Mais uma vez, em caso de erro, a varivel global errno actualizada podendo o seu significado
ser apresentado ao utilizador atravs da funo perror().
No valor de status vm codificada bastante informao, nomeadamente: os 8 bits menos
significativos que foram passados como parmetro funo exit() (chamada pelo filho), o sinal
que provocou o fim (anormal) do processo filho, se o fim do filho deu origem a um ficheiro de core,
etc.
Contudo, a forma mais fcil de extrair essa informao atravs de macros especficas.
A macro WIFEXITED(status) devolve verdadeiro se o filho retornou normalmente e nessa
altura pode-se chamar outra macro, WEXITSTATUS(status), que vai retornar os 8 bits menos
significativos que foram passados como parmetro funo exit().
O exemplo seguinte cria um filho, que aps ficar suspenso de execuo durante 5 segundos, termina
retornando o nmero 5. O pai espera sem bloquear que o filho termine. A funo sleep() usada
neste programa permite suspender a execuo de um processo durante x segundos.
#include
#include
#include
#include
#include
<stdio.h>
<unistd.h>
<sys/wait.h>
<sys/types.h>
<stdlib.h>
int main(void)
{
pid_t pid;
int aux;
int status;
Pgina 12 de 14
pid=fork();
if (pid<0)
{
perror("Erro ao cria o processo\n");
exit(-1);
}
else
{
if (pid > 0) /* Cdigo do Pai */
{
printf("Pai\n");
do
{
aux = waitpid(pid, &status, WNOHANG);
if (aux==-1)
{
perror("Erro em waitpid");
exit(-1);
}
if (aux == 0)
{
printf(".\n");
sleep(1);
}
} while (aux == 0);
if (WIFEXITED(status))
{
printf("Pai: o filho retornou o valor:%d\n", WEXITSTATUS(status));
}
}
else /* Cdigo do filho */
{
printf("Filho\n");
sleep(5);
printf("Filho a sair\n");
exit(5);
}
exit(0);
}
}
Figura 7 Exemplo de fork(), exit() e waitpid()
Note-se que no cdigo da Figura 7 foram omitidas a maior parte das verificaes de erros de modo a
simplificar o exemplo.
A relao entre processo pais e filhos pode ser representada atravs de um rvore de processos. Nesta tipo
de rvores, os processos so representados por linhas verticais, caso necessrio a execuo de uma
determinada linha de cdigo tambm pode ser representada na mesma linha atravs de uma pequena linha
horizontal. A execuo de um fork, e a criao de um processo filho representada por uma derivao
linha principal. A finalizao de um processo representada por uma circunferncia a cheio. A comunicao
entre os processos (troca de sinais, acessos memria partilhada, etc) representada atravs de setas. A
Figura 8, mostra a rvore de processos para o programa da Figura 7.
Pgina 13 de 14
Pai
fork()
Filho
printf(Pai)
printf(Filho)
waitpid()
exit(5)
5 Bibliografia
Orlando Sousa, Processos, Apontamentos das aulas prticas de SOP2, Instituto Superior de
Engenharia do Porto, 2004.
W. Richard Stevens, Advanced Programming in the UNIX Environment, Addison Wesley, 1994
John Shapley Gray, Interprocess Communications in UNIX The Nooks & Crannies, 2nd Edition,
Prentice Hall, 1998
Steve McConnell, CODE Complete: A Practical Handbook of Software Construction, Microsoft
Press Books, 1993
Pgina 14 de 14