- Escreva um programa que imprime na sua saída standard os argumentos com que é invocado bem como o valor da sua variável de ambiente SHELL.
- Dicas:
-
- Desenvolva o programa gradualmente. Inicialmente, desenvolva uma versão que imprime todas as variáveis de ambiente e os respectivos valores. Pode usar um formato semelhante ao usado pelo comando printenv.
- Para comparar strings use funções da biblioteca C (
man 3 string
)
- Informação:
- Pode alterar as variáveis de ambiente dando os seguintes comandos, conforme a shell que usa:
- bash
-
<nome_variável>=<valor_variável>; export <nome_variável>
- (t)csh
-
setenv <nome_variável> <valor_variável>
Para mais informação sobre estas shells pode consultar uma comparação sumária entre a bash e a tcsh compilada pelo Prof. José Manuel Cruz.
- Escreva um programa que:
- Lista as suas variáveis de ambiente e os respectivos valores num ficheiro, cujo nome lhe é passado como argumento;
- Cria um processo filho, que lista as suas variáveis de ambiente e os respectivos valores noutro ficheiro, cujo nome é passado como argumento quer ao processo pai quer ao processo filho.
Compare os ficheiros criados pelos 2 processos. Que conclusões pode tirar em relação às variáveis de ambiente dum processo e do seu filho?
- Escreva um programa que cria um filho e um neto. Cada um dos processos deve escrever o seu pid e o do seu pai, além do código de terminação do seu filho (se existir). O filho e o neto devem terminar com o código 2 e 3, respectivamente.
Dica
Desenvolva o programa gradualmente. P.ex.:
- Desenvolva uma versão na qual o processo único deverá imprimir o seu pid e o
do seu pai.
- Desenvolva uma versão na qual o processo "inicial" cria um processo filho. O processo pai deverá imprimir o pid do seu filho e terminar. O processo filho deverá imprimir o seu pid e o do seu pai.
- Desenvolva uma nova versão com as seguintes diferenças: (i) O processo pai deverá aguardar pela terminação do processo filho e imprimir o código de terminação deste último. (ii) O processo filho deverá terminar com o código 2.
- Escreva um programa que cria um processo que compila um programa em C, cujo nome lhe é passado como argumento da linha de comandos. O seu programa deverá executar o utilitário gcc (/usr/bin/gcc), invocando a chamada ao sistema execve(), com os argumentos apropriados. O processo pai deverá esperar que o processo filho termine.
- Crie uma versão do programa anterior, tal que o programa execute ele próprio o gcc, i.e.
que não crie um processo filho para realizar essa tarefa.
- Escreva um programa para demonstrar o processamento de sinais POSIX.
- Numa primeira versão, o seu programa deverá apenas solicitar ao utilizador que introduza dados através da entrada standard, e tentar lê-la usando a chamada ao sistema read().
Dica: Para testar o seu programa, execute-o e, em vez de introduzir "texto" através do teclado, envie-lhe um sinal SIGINT, premindo CTRL-C. Procure explicar o que observa.
- Crie uma nova versão do seu programa que deverá instalar um handler que "processe" sinais do tipo SIGINT. Este handler pode ser apenas uma função sem qualquer instrução. Para melhor ilustrar o funcionamento de sinais, o seu programa deverá invocar a função printf() para indicar o processamento que realiza. Deverá ainda fazer um processamento cuidadoso do valor retornado por read().
IMP. O seu handler não deverá invocar a função printf() pois esta pode não ser async-signal-safe (para mais informação tente man 7 signal).
Para testar o processamento de sinais: (i) execute o seu programa e proceda como na alínea anterior, i.e. prima CTRL-C sem premir previamente outras teclas; (ii) execute o seu programa e prima algumas teclas, mas não a tecla ENTER, e depois prima CTRL-C.
Que conclusões tira sobre o processamento de sinais e sobre o efeito da sua ocorrência na execução duma chamada
ao sistema como read() que bloqueia à espera de eventos gerados por periféricos. Leia a man page de
read para validar as suas conclusões.
- Neste problema deverá escrever um programa para ilustrar o processamento de sinais POSIX gerados pelo próprio SO, nomeadamente o sinal SIGCHLD que é gerado quando um processo filho termina (ou é suspenso).
- Numa primeira versão, o processo pai deverá instalar um handler para aquele sinal, criar um processo filho e executar sigsuspend(), aguardando que o processo filho termine.
O processo filho deverá invocar a função sleep() (tente man 3 sleep) com argumento 1 ou 2, e depois terminar.
Tanto o processo pai como o seu filho deverão invocar a função printf() com argumentos apropriados para que se possa seguir os passos que executam.
- Altere o programa desenvolvido na alínea anterior de modo a que o processo filho termine imediatamente, i.e. sem executar sleep(), e o processo pai invoque sleep() antes de invocar sigsuspend
Procure explicar os resultados obtidos executando as 2 versões do seu programa.
- Este problema pretende ilustrar o envio dum sinal por um processo a outro.
- Numa primeira versão o processo pai deverá criar o processo filho, invocar sleep() com argumento 1 ou 2, e em seguida enviar o sinal SIGUSR1 ao processo filho, invocando a chamada ao sistema kill(). Em seguida, deverá esperar pela terminação do processo filho, via wait().
O processo filho deverá invocar sigsuspend(). Quando retornar desta chamada ao sistema deverá invocar exit() com o valor de errno após retorno de sigsuspend().
Tanto o processo pai como o seu filho deverão invocar a função printf() com argumentos apropriados para que se possa seguir os passos que executam. Em particular, o processo pai deverá fornecer informação detalhada sobre o valor recebido através do argumento de exit() (veja a man page de wait() e o exemplo nela incluido).
- Altere o programa desenvolvido na alínea anterior de modo a que o processo pai instala um handler para processar sinais SIGUSR1 antes de criar o processo filho.
Procure explicar os resultados obtidos executando as 2 versões do seu programa.