Skip to content

Commit

Permalink
aula16
Browse files Browse the repository at this point in the history
  • Loading branch information
flubacheski committed Apr 22, 2024
1 parent 1993c24 commit 66ba569
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 2 deletions.
9 changes: 9 additions & 0 deletions material/aulas/16-sinais-I/ex1_slide.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdio.h>
int b=0;
int main()
{
int i = 3/b;
printf("fim do programa.\n");
return 0;
}

11 changes: 11 additions & 0 deletions material/aulas/16-sinais-I/ex2_slide.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

#include <stdio.h>

int main() {
int *px = (int*) 0x01010101;
*px = 0;
printf("fim do programa.\n");
return 0;
}


104 changes: 104 additions & 0 deletions material/aulas/16-sinais-I/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# 16 - Enviando sinais

Na aula de hoje falamos sobre sinais e vimos que grande parte deles indica que algo excepcional aconteceu. Veremos neste handout as chamadas usadas para consultar o status de um processo quando ele acaba com erro e como enviar sinais para outros processos.

## Recuperando informações de erros usando `wait`

Anteriormente vimos que ao chamar `wait(&status);` guardamos informações sobre o fim do processo filho na variável `status`. Nos outros exercícios olhamos para os casos em que `WIFEXITED(status) == 1`.

```C
int wstatus;

wait(&wstatus);
printf("Filho acabou\n");

// Utiliza macro para ler um "pedaço" (um ou mais bits) de wstatus
printf("Terminou normal?: %d\n", WIFEXITED(wstatus)); // lê 0 ou 1

// se terminou normalmente
if (WIFEXITED(wstatus)) {
// lê o byte menos significativo do return do filho
printf("Valor de retorno: %d\n", WEXITSTATUS(wstatus));
}
```
Todo término inesperado de um programa em execução (processo) é feito usando um sinal. Ao acessar informações em um local de memória não mapeado para o nosso processo ele recebe o sinal `SIGSEGV`. Ao executar uma divisão por zero ele receberá o sinal `SIGFPE`. Logo, nestes casos é verdade que `WIFSIGNALED(status) == 1`, então podemos pegar o número do sinal usando a macro `WTERMSIG(status)`.
!!! exercise text short
Leia o arquivo *parte1.c*. O que ele faz? Quantos processos são criados (conte o original)?
!!! answer "Resposta"
O programa original cria um processo filho, que faz uma divisão por zero. São dois processos, portanto.
!!! example
Modifique *parte1.c* para o processo pai esperar o fim do filho e mostrar uma mensagem com o `pid` do filho que acabou (pegue isto via `wait`).
!!! example
No proceso pai, após o `wait` mostre no terminal as seguintes expressões:
* `WIFEXITED(status)`
* `WIFSIGNALED(status)`
* `WTERMSIG(status)`
!!! example "To-Do"
Mostrar o número do sinal não é muito útil. Pesquise sobre a chamada `strsignal` e use-a para mostrar uma mensagem descritiva de qual sinal foi recebido no exercício acima.
!!! tip
Após cada modificação no código, compile e execute para conferir os resultados!
## Envio de sinais via terminal
Além de erros e exceções, sinais também são usados para avisar de mudanças no sistema, sejam elas iniciadas pelo usuário ou por outros processos. A sequência de exercícios abaixo é um experimento de envio de sinais.
!!! example
Faça uma cópia do arquivo *parte1.c* e salve como *parte2.c*. No novo arquivo, altere para que o processo filho mostre seu pid e entre em loop infinito.
Claramente nem o pai nem o filho terminam no exemplo acima. Porém, se o filho terminar o pai termina também! O sinal **SIGKILL** é usado para terminar forçadamente um processo e ele pode ser enviado por qualquer outro processo do mesmo usuário (ou o *root*, que pode mandar sinais para qualquer processo do sistema).
O envio de sinais é feito usando a chamada `kill`. Assim como outras chamadas de sistema, `kill` possui também um programa de linha de comando.
!!! example
Abra dois terminais e coloque um ao lado do outro. No primeiro, execute o programa *parte2.c*.
!!! example
No segundo terminal, use a ferramenta de linha de comando `kill` para enviar o sinal **SIGKILL** para o processo filho. Se precisar, consulte a documentação usando `man 1 kill`.
!!! exercise text short
Use `man 7 signal` para ver a lista de sinais disponíveis e seus números. Qual seria o número para o sinal `SIGINT`?
!!! answer "Resposta"
Execute `man 7 signal`e procure por **Signal numbering for standard signals**
Isto deve ter feito o pai finalizar e mostrar a informação de que o processo filho foi finalizado. Note que o pai tem direito a saber o sinal usado para finalizar um filho:
!!! example
Envie o sinal `SIGINT` para seu processo filho e verifique que o processo pai mostra o número correto.
## Envio de sinais em um programa
O programa `kill` é apenas um casquinha em volta de sua chamada de sistema.
!!! example
Veja a documentação da chamada de sistema (em C) no manual `man 2 kill`
!!! example
Faça uma cópia do arquivo *parte2.c* e salve como *parte3.c*. Modifique o novo arquivo para que o processo pai espere 10 segundos e envie um **SIGKILL** para o filho.
!!! exercise text short
Agora os dois processos acabam? As informações de finalização no pai condizem com o sinal enviado?
!!! answer "Resposta"
Se restarem dúvidas, confira com o professor!
### Extra
Até o momento usamos a chamada `wait` para esperar um processo filho acabar. Porém, não sabemos ainda checar **se** um processo filho acabou (um processo filho específico, sem necessariamente deixar a chamada `wait` travada)! Podemos fazer isso com a função `waitpid`, que recebe o `pid_t` do processo filho, e permite esperar ou não pela finalização dele.
!!! example
Pesquise no manual pela flag `WNOHANG` da chamada `waitpid`.
!!! example
Faça uma cópia do arquivo *parte3.c* e salve como *parte4.c*. Modifique o novo arquivo para que o processo pai só envie o sinal se o processo filho ainda estiver executando. Salve como *parte4.c*
15 changes: 15 additions & 0 deletions material/aulas/16-sinais-I/parte1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>

int main() {
pid_t filho;

filho = fork();
if (filho == 0) {
int i = 1/0;
printf("Divisão por zero!\n");
}

return 0;
}
Binary file added material/aulas/16-sinais-I/slides.pdf
Binary file not shown.
116 changes: 116 additions & 0 deletions material/labs/processos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Lab de Processos

Nesse lab iremos trabalhar com bibliotecas, integração entre processos e multiprocessamento para a criação de um programa similar ao [`wget`](https://www.gnu.org/software/wget/). Nosso programa, chamado de `web_downloader`, fará o download de arquivos referênciados por uma URL, para tanto, utilizaremos a ferramenta `cURLInsper`.

A ferramenta `cURLInsper` é um executável de linha de comando que transfere dados de um servidor usando o protocolo HTTP. Para fazermos o download de uma URL podemos executar o `cURLInsper` no terminal, por exemplo:

`./cURLInsper https://insper.github.io/SistemasHardwareSoftware/index.html index.txt`

No exemplo, estamos tentando fazer o download da página `https://insper.github.io/SistemasHardwareSoftware/index.html/` e armazenando o arquivo `index.html` no arquivo `index.txt`.

O executável `cURLInsper` pode ter os seguintes retornos (return) em sua execução:

* Quando a **quantidade de parâmetros está incorreta**, o programa **retorna -1**;

* Quando um **download** é bem sucedido, o programa **retorna 0**; e

* Quando a URL não existe ou ocorre uma **falha no download**, o programa **retorna 1**.

Ultimament o executável `cURLInsper` está um pouco instável com um comportamento estranho (culpa talvez do último programador que deu manutenção no código), essa manutenção fez com que, as vezes, a ferramenta demore um tempo excessivo para realizar o download ou até mesmo falhe durante ele de forma inexplicável.

O programador que fez o última manutenção disse que quando o download falhar, o usuário pode executar novamente a ferramenta informando o número da tentiva, incrementando esse número a cada nova tentativa, até que o download seja bem sucedido, e acreditem, essa gambiarra funciona. Para o exemplo abaixo temos um erro de download.

`./cURLInsper https://insper.github.io/SistemasHardwareSoftware/index.html index.html`

Mas se passar o número da tentativa igual a `1` no último parâmetro da chamada do `cURLInsper`, o download funciona:

`./cURLInsper https://insper.github.io/SistemasHardwareSoftware/index.html index.html 1`

## Restrições

Este exercício serve como avaliação dos conceitos vistos na disciplina. Portanto, algumas restrições serão aplicadas ao código de vocês:

- todo trabalho com arquivos deverá ser feito usando as APIs POSIX vistas em aula. **Não é permitido o uso de funções da** `Standard I/O` para manipulação de arquivos, como por exemplo `fopen()`, `fread()` e `fclose()`.
- se você usar algum trecho de código da documentação (ou de outra fonte), coloque uma atribuição em um comentário no código.
- Fica proibido o uso de ferramentas de geração de código automático por IA, como por exemplo o ChatGPT.

## Entrega

Você deverá colocar sua entrega na pasta `lab/02-lab-processos` em seu repositório de atividades, na branch principal. Não precisa soltar release/tag. Além disso, grave um vídeo mostrando a tela do funcionamento do seu programa apresentando cada uma das fases desenvolvidas, e por fim, **preencha o arquivo `README.md` informando até qual fase você conseguiu chegar e o link do vídeo do funcionamento da sua entrega**. Lembre-se de se atentar ao prazo de entrega definido [aqui!](../../sobre).

## Avaliação

O programa será avaliado de forma manual usando uma rubrica que descreve as funcionalidades implementadas. Quanto maior o número de funcionalidades maior será a nota.

### **Fase 0**

- O programa não compila
- Não preencheu o arquivo `README.md`
- O programa não implementa algum dos requisitos da rubrica da fase **1**.

**NOTA desta fase**: 0.0

### **Fase 1**

- O programa compila com warnings.
- O programa roda na linha de comando como abaixo:

`./web_downloader http://exemplo.com/pagina.html`

- A página acima deve ser salva em um arquivo com o seguinte formato: `exemplo_com_pagina.html`, ou seja, ignorando `http://` e `https://`, substituindo todas barras e pontos (exceto o último) por `_`.

- Para fazer o download da URL informada o seu programa `web_downloader` deve fazer uma chamada do excutável `cURLInsper` utilizando a função `exec` conforme visto em aula, você não pode, por exemplo, usar a chamada da função **system**.
- Ao terminar de baixar uma página, caso tenha sucesso, você deverá mostrar a mensagem `{url} baixada com sucesso!`. Exemplo: `{https://insper.github.io/SistemasHardwareSoftware/index.html} baixada com sucesso!`
- Se o download falhar o seu programa deverá mostrar a mensagem `{url} não pode ser baixada.`. Exemplo: `{https://insper.github.io/SistemasHardwareSoftware/index.html} não pode ser baixada.`

**NOTA desta fase**: 2.0

### **Fase 2**

- O programa compila sem warnings.
- Não entregou o video explicando o funcionamento do programa.
- O programa recebe uma flag `-f` seguida pelo nome de um arquivo. Seu programa deverá ler o arquivo e fazer o download de cada url dentro do arquivo. Você pode supor que cada linha do arquivo contém exatamente uma URL. As regras para o nome do arquivo correspondente são as mesmas do item anterior.
- Nessa fase cada download das URLs do arquivo informado por linha de comando pode ser realizada de forma sequencial, abaixo um exemplo da chamada do seu programa `web_downloader` com o flat `-f`.

`./web_downloader -f lista_download.txt`

**NOTA desta fase**: 4.0

### **Fase 3**
- Video explicando e mostrando a execução do programa em paralelo.
- O download de cada URL é feito em um processo separado e em paralelo.
- Se o download falhar, por alguma razão, o seu programa deve realizar pelo menos mais duas tentativas de chamadas do executável `cURLInsper`, informando o número da tentativa na chamada do `cURLInsper`. A cada tentativa deve ser informado se conseguiu ou não baixar a URL.

**NOTA desta fase**: 6.0

### **Fase 4**

- O processo principal só termina depois que todos os arquivos foram baixados.
- Programa roda **sem erros no valgrind**.
- O programa abre até `N` processos em paralelo. Se houver mais que `N` urls então os processos deverão sempre existir no máximo `N+1` processos (`N` para fazer download mais o original). Esse valor é passado pela linha de comando via flag `-N`, conforme abaixo:

`./web_downloader -f lista_download.txt -N 2`

Se nada for passado assuma `N=4`, por exemplo:

`./web_downloader -f lista_download.txt -N`


**NOTA desta fase**: 8.0

### **Fase 5**

- As mensagens de finalização de baixar uma página são mostradas sem estarem embaralhadas, mesmo caso vários processos terminarem ao mesmo tempo.
- Ao apertar Ctrl+C todas as transferências são paradas e os arquivos que não foram baixados até o fim são deletados e os processos filhos são encerrados.


**NOTA desta fase**: 10.0

**IMPORTANTE:** Considere as **fases como cumulativas**, ou seja, cada versão deve **manter as funcionalidades da fase anterior** e acrescentar novas. Por exemplo, a versão da **Fase 2**, além de ler o arquivo com várias URL, também deve funcionar com download direto passando apenas a URL (sem `-f arquivo`).

Caso você não implemente alguma funcionalidade de uma determinada fase a nota atribuída será a da fase anterior.


### Prazo:

[Clique aqui!](../../sobre).
1 change: 1 addition & 0 deletions material/sobre.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
| 08/04 | Atv09-TAD | Github | 12/04 23h59 |
| 11/04 | Atv10-processos | Github | 18/04 23h59 |
| 18/04 | Atv11-entrada-saida | Github | 22/04 23h59 |
| 22/04 | Lab02-processos | Github | 06/05 23h59 |



Expand Down
4 changes: 2 additions & 2 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ nav:
- aulas/13-processos/index.md
- aulas/14-exec/index.md
- aulas/15-entrada-saida/index.md
#- aulas/16-sinais-I/index.md
- aulas/16-sinais-I/index.md
#- aulas/17-sinais-II/index.md
#- aulas/18-threads-I/index.md
#- aulas/19-sincronizacao/index.md
Expand All @@ -116,7 +116,7 @@ nav:
#- aulas/22-questoes-de-revisao/index.md
- Labs:
- labs/hackerlab.md
#- labs/processos.md
- labs/processos.md
#- labs/threads.md
- Dicas:
- outros/github.md
Expand Down

0 comments on commit 66ba569

Please sign in to comment.