Mudanças entre as edições de "SOP-EngTel (página)"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
(SOP29005 2014-1)
(SOP29005 2014-2)
Linha 1: Linha 1:
= Publicada originalmente como [[SOP29005-2014-1]] =
+
= Publicada originalmente como [[SOP29005-2014-2]] =
  
= EngTel: Sistemas Operacionais - Diário de Aulas 2014-1 =
+
= EngTel: Sistemas Operacionais - 2014-2 =
  
'''Professor:''' [[Arliones Hoeller]]
+
*'''Professor:''' [[Arliones Hoeller]]
<br>'''Turma:''' 29005
+
*'''Turma:''' 29005
<br>'''Encontros:''' terças e sextas às 9:40.
+
*'''Encontros:''' quartas e quintas das 15:40 às 17:30
<br>'''Atendimento paralelo:''' segundas às 13:30 e quintas às 8:25.
+
*'''Atendimento paralelo:''' quintas das 9:40 às 10:35 e das 14:25 às 15:20
<br> ''Endereço web do grupo'': https://www.facebook.com/groups/sop29005.ifsc.sj/
+
*'''Grupo de discussão'''
<br> ''Endereço de e-mail da disciplina'': sop29005.ifsc.sj@groups.facebook.com
+
** ''Web'': https://www.facebook.com/groups/sop29005.ifsc.2014.2/
<br>
+
** ''Email'': sop29005.ifsc.2014.2@groups.facebook.com
<br>Outros cursos de sistemas operacionais nos quais este curso se baseia:
+
 
*[http://www.lisha.ufsc.br/teaching/os/ Sistemas Operacionais - Ciências da Computação UFSC]
+
*Outros cursos de sistemas operacionais nos quais este curso se baseia:
*[http://dainf.ct.utfpr.edu.br/~maziero/doku.php/so:start Sistemas Operacionais - Engenharia da Computação UTFPR]
+
**[http://www.lisha.ufsc.br/teaching/os/ Sistemas Operacionais - Ciências da Computação UFSC]
*[http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-828-operating-system-engineering-fall-2006/ Operating Systems Engineering - Computer Science MIT]
+
**[http://dainf.ct.utfpr.edu.br/~maziero/doku.php/so:start Sistemas Operacionais - Engenharia da Computação UTFPR]
 +
<!--**[http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-828-operating-system-engineering-fall-2006/ Operating Systems Engineering - Computer Science MIT]-->
  
 
== [[SOP-EngTel_(Plano_de_Ensino)|'''Plano de ensino''']] ==
 
== [[SOP-EngTel_(Plano_de_Ensino)|'''Plano de ensino''']] ==
  
 +
== [[Cronograma de atividades (SOP-EngTel)|'''Cronograma de Atividades - Planejado''']] ==
  
== Cronograma de Trabalhos Práticos ==
+
== '''Cronograma de Atividades - Ajustado''' ==
  
{| class="wikitable"
+
{{Cronograma-top}}
|-
+
{{Cl|1 |31/7 | 2 | Apresentação da Disciplina. Visão geral de funções, responsabilidades e estruturas de um SO | Lab. Informática}}
! Trabalho
+
{{Cl|2 |6/8 | 2 | Atividades em laboratório: Introdução ao Linux e GCC | Lab. Informática}}
! Descrição
+
{{Cl|3 |7/8 | 2 | Arquitetura de sistemas operacionais e modelos de programação | Lab. Informática}}
! Lançamento
+
{{Cl|4 |13/8 | 2 | Gerência de tarefas; contextos, processos e threads | Lab. Informática}}
! Entrega
+
{{Cl|5 |14/8 | 2 | Atividades em laboratório: API POSIX – fork/wait – t0: biblioteca de filas | Lab. Informática}}
|-
+
{{Cl|6 |20/8 | 2 | Escalonamento de tarefas | Lab. Informática}}
| t0
+
{{Cl|7 |21/8 | 2 | Atividades em laboratório: Estrutura de processos (a verdadeira história do Hello World) – t1: troca de contexto e tarefas cooperativas | Lab. Informática}}
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t0:_Biblioteca_de_Filas Biblioteca de filas]
+
{{Cl|8 |27/8 | 2 | Atividade em laboratório: acompanhamento de projetos | Lab. Informática}}
| 21/02/2014
+
{{Cl|9 |28/8 | 2 | Algoritmos de escalonamento | Lab. Informática}}
| 14/03/2014
+
{{Cl|10 |3/9 | 2 | Algoritmos de escalonamento / Revisão para prova | Lab. Informática}}
|-
+
{{Cl|11 |4/9 | 2 | Revisão e correção de listas de exercícios | Lab. Informática}}
| t1
+
{{Cl|12 |10/9 | 2 | P0 (introdução e gerência de tarefas) | Lab. Informática}}
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t1:_Troca_de_contexto_em_n.C3.ADvel_de_usu.C3.A1rio Troca de contexto]
+
{{Cl|13 |11/9 | 2 | Atividades em laboratório: pthreads – t2: escalonamento FIFO e por prioridades | Lab. Informática}}
| 28/02/2014
+
{{Cl|14 |17/9 | 2 | Comunicação entre processos: Troca de mensagens e Memória Compartilhada | Lab. Informática}}
| 21/03/2014
+
{{Cl|15 |18/9 | 2 | Atividade em laboratório: programação com pipes e API shm – t3: Preempção e compartilhamento de tempo | Lab. Informática}}
|-
+
{{Cl|16 |24/9 | 2 | Coordenação entre processos | Lab. Informática}}
| t2
+
{{Cl|17 |25/9 | 2 | Laboratório: Problemas clássicos de coordenação; impasses | Lab. Informática}}
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t2:_Estruturas_de_tarefas Biblioteca de tarefas]
+
{{Cl|18 |1/10 | 2 | Laboratório: Problemas clássicos de coordenação; impasses | Lab. Informática}}
| 28/02/2014
+
{{Cl|19 |2/10 | 2 | Laboratório: Problemas clássicos de coordenação; impasses | Lab. Informática}}
| 21/03/2014
+
{{Cl|20 |8/10 | 2 | Laboratório: Problemas clássicos de coordenação; impasses | Lab. Informática}}
|-
+
{{Cl|21 |9/10 | 2 | Laboratório: Problemas clássicos de coordenação; impasses | Lab. Informática}}
| t3
+
{{Cl|22 |15/10 | 2 | Encaminhamento para palestra MCC | Lab. Informática}}
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t3:_Despachante_de_tarefas Despachante de tarefas]
+
{{Cl|23 |16/10 | 2 | Revisão e correção de listas de exercícios | Lab. Informática}}
| 24/03/2014
+
{{Cl|24 |22/10 | 2 | P1 (comunicação e coordenação de tarefas) | Lab. Informática}}
| 11/04/2014
+
{{Cl|25 |23/10 | 2 | Gerenciamento de memória: Introdução | Lab. Informática}}
|-
+
{{Cl|26 |29/10 | 4 | Gerenciamento de memória: paginação e segmentação. | Lab. Informática}}
| t4
+
{{Cl|27 |30/10 | 2 | Atividade em laboratório: alocação de memória. | Lab. Informática}}
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t4:_Escalonamento_por_prioridades Escalonamento por prioridade]
+
{{Cl|28 |5/11 | 2 | Aula adiantada em 29/out | Lab. Informática}}
| 28/03/2014
+
{{Cl|29 |6/11 | 0 | Aula adiada: viagem do professor | Lab. Informática}}
| 11/04/2014
+
{{Cl|30 |12/11 | 2 | Gerenciamento de memória: memória virtual | Lab. Informática}}
|-
+
{{Cl|31 |13/11 | 2 | Gerenciamento de memória: memória virtual | Lab. Informática}}
| t5
+
{{Cl|32 |19/11 | 2 | Revisão e correção de listas de exercícios | Lab. Informática}}
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t5:_Preemp.C3.A7.C3.A3o_e_compartilhamento_de_tempo Preempção e compartilhamento de tempo]
+
{{Cl|33 |20/11 | 2 | P2 (gerenciamento de memória) | Lab. Informática}}
| 31/03/2014
+
{{Cl|34 |26/11 | 2 | Sistemas de arquivos: introdução e controle de acesso | Lab. Informática}}
| 14/04/2014
+
{{Cl|35 |27/11 | 2 | Sistemas de arquivos: estudos de caso e gerenciamento de memória secundária | Lab. Informática}}
|-
+
{{Cl|36 |3/12 | 2 | Gerenciamento de entrada e saída: introdução / Atividade em laboratório: construção de módulo para Linux | Lab. Informática}}
| t6
+
{{Cl|37 |4/12 | 2 | Revisão e correção de listas de exercícios | Lab. Informática}}
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t6:_Contabiliza.C3.A7.C3.A3o_de_tarefas Contabilização de tarefas]
+
{{Cl|38 |10/12 | 2 | P3 (sistemas de arquivo e gerenciamento de entrada e saída) | Lab. Informática}}
| 31/03/2014
+
{{cronograma-botton |76}}
| 14/04/2014
 
|-
 
| t0 - t6
 
| Apresentação dos trabalhos
 
| -
 
| 14/04/2014
 
|-
 
| t7
 
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t7:_Tarefa_main Tarefa main]
 
| 05/05/2014
 
| 30/05/2014
 
|-
 
| t8
 
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t8:_Operador_Join Operador join]
 
| 05/05/2014
 
| 30/05/2014
 
|-
 
| t9
 
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#t9:_Chamada_Sleep Chamada sleep]
 
| 05/05/2014
 
| 30/05/2014
 
|-
 
| tA
 
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#tA:_Constru.C3.A7.C3.A3o_de_Sem.C3.A1foros Construção de Semáforos]
 
| 30/05/2014
 
| 13/06/2014
 
|-
 
| tB
 
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#tB:_Uso_de_Sem.C3.A1foros Uso de semáforos]
 
| 30/05/2014
 
| 13/06/2014
 
|-
 
| tC
 
| [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#tC:_Fila_de_Mensagens Fila de mensagens]
 
| 30/05/2014
 
| 13/06/2014
 
|-
 
| t7 - tC
 
| Apresentação dos trabalhos
 
| -
 
| 16/06/2014
 
|-
 
| RT
 
| Trabalho de recuperação: reapresentação individual de todos os projetos.
 
| 16/06/2014
 
| 10/07/2014
 
|}
 
  
 
== Notas ==
 
== Notas ==
Linha 114: Linha 69:
 
! Matrícula
 
! Matrícula
 
! P0
 
! P0
! R0
 
 
! P1
 
! P1
! R1
+
! P2
 +
! P3
 
! T0
 
! T0
 
! T1
 
! T1
! RT
 
 
! Final
 
! Final
 
|-
 
|-
| 121000556-5
+
| 122001838-4 || A || A || B || B || A || B || A
| D
 
| B
 
| B
 
| B
 
| C
 
| D
 
| B
 
| B
 
 
|-
 
|-
| 121003145-0
+
| 122001432-0 || A || B || B || A || A || B || A
| D
 
| A
 
| A
 
| A
 
| A
 
| A
 
|  
 
| A
 
 
|-
 
|-
| 121001036-4
+
| 122005026-1 || A || B || A || B || A || B || A
| D
 
| C
 
| D
 
| B
 
| A
 
| A
 
|  
 
| B
 
 
|-
 
|-
| 121003013-6
+
| 122006899-3 || B || A || C || A || A || C || B
| B
 
| B
 
| B
 
| B
 
| A
 
| A
 
|  
 
| A
 
 
|-
 
|-
| 121003282-1
+
| 122001913-5 || B || A || A || B || A || C || B
| D
+
|}
| C
+
 
| A
+
= Material de aula =
| A
+
 
| D
+
== Slides ==
| B
+
 
| C
+
* [[Arquivo:SOP29005-parte1.pdf]]
| C
+
* [[Arquivo:SOP29005-parte2.pdf]]
|-
+
* [[Arquivo:SOP29005-parte3.pdf]]
| 121001865-9
+
* [[Arquivo:SOP29005-parte4.pdf]]
| C
+
* [[Arquivo:SOP29005-parte5.pdf]]
| C
+
* [[Arquivo:SOP29005-parte6.pdf]]
| B
+
* [[Arquivo:SOP29005-parte7.pdf]]
| B
+
 
| C
+
== Listas de exercícios ==
| D
+
 
| B
+
As listas de exercícios são compostas por exercícios selecionados do livro do Silberschatz, 8a edição. Há 10 volumes deste livro na biblioteca do campus, sendo suficiente para toda a turma deste semestre.
| B
 
|-
 
| 121000526-3
 
| C
 
| A
 
| C
 
| C
 
| A
 
| A
 
|
 
| A
 
|-
 
| 121000088-1
 
| C
 
| C
 
| B
 
| B
 
| A
 
| A
 
|
 
| B
 
|-
 
| 121001105-0
 
| C
 
| A
 
| B
 
| B
 
| A
 
| A
 
|
 
| A
 
|-
 
|  121003758-0
 
| D
 
| B
 
| C
 
| C
 
| A
 
| A
 
|
 
| B
 
|-
 
|  121000653-7
 
| D
 
| A
 
| C
 
| B
 
| D
 
| B
 
| B
 
| B
 
|-
 
|  121003004-7
 
| C
 
| C
 
| C
 
| C
 
| A
 
| A
 
|
 
| B
 
|-
 
| 121001065-8
 
  
| F
+
SILBERSCHATZ, Abraham; GALVIN, Peter; GAGNE, Greg. Fundamentos de sistemas operacionais. 8. ed. Rio de Janeiro: LTC, 2011. 515 p., il. ISBN 9788521617471.
| F
 
| F
 
| F
 
| F
 
| F
 
| F
 
| FI
 
|-
 
|}
 
  
== Relação de Entrega dos Trabalhos Práticos ==
+
Exercícios selecionados:
  
{| class="wikitable"
+
*Parte 1
|-
+
**Capítulo 1: 1-3, 6-8, 10, 13, 14, 17, 22, 23, 25.
! Grupo
+
**Capítulo 2: 1-8, 12, 13, 15, 17, 22, 25.
! t0
+
*Parte 2
! t1
+
**Capítulo 3: 1, 3, 6-10, 13, 15
! t2
+
**Capítulo 4: 1, 4, 7, 8, 10-13
! t3
+
**Capítulo 5: 1-3, 5, 6, 9, 10, 13-15, 21
! t4
+
*Parte 3
! t5
+
**Capítulo 6: 1, 2 (utilizar semáforos POSIX), 6, 8, 11-15, 18, 20, 21, 25, 29, 39.
! t6
+
*Parte 4
! t7
+
**Capítulo 8: 1-6, 9-21, 23.
! t8
+
**Capítulo 9: 1-8, 14-16, 19-23, 28.
! t9
+
*Parte 5
! tA
+
**Capítulo 10: 1-20
! tB
+
**Capítulo 11: 1-7
! T0
+
**Capítulo 12: 1-7, 13-14 (desafio).
! T1
 
! REC
 
|-
 
| 121000556-5, 121001865-9
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| Zz
 
| Zz
 
| Zz
 
| Zz
 
| Zz
 
| Zz
 
| Zz
 
| C
 
| D
 
| B,B
 
|-
 
| 121003145-0, 121003013-6
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| A
 
| A
 
| -
 
|-
 
| 121001036-4, 121003004-7
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| A
 
| A
 
| -
 
|-
 
| 121003282-1, 121000653-7
 
| OK
 
| OK
 
| Zz
 
| Zz
 
| Zz
 
| Zz
 
| Zz
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| D
 
| B
 
| C,B
 
|-
 
| 121000526-3, 121001105-0
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| A
 
| A
 
| -
 
|-
 
| 121000088-1, 121003758-0
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| OK
 
| A
 
| A
 
| -
 
|}
 
  
OK - Trabalho entregue.
+
== Projeto ==
  
Zz - Dormiu no ponto e não entregou.
+
Esta disciplina utiliza um projeto contínuo, no qual os alunos desenvolvem um aplicativo que emula um sistema operacional no espaço de usuário no Linux. Este projeto é chamado de [[BOOOS - Basic Object Oriented Operating System]].
  
 
=Diário de Aulas=
 
=Diário de Aulas=
  
== 11/02: Apresentação da disciplina. Histórico, visão geral e estruturas de um SO ==
+
== 31/07: Apresentação da Disciplina. Visão geral de funções, responsabilidades e estruturas de um SO ==
  
 
* [https://www.youtube.com/watch?v=7LGKgdWtrqI Revolution OS]: documentário sobre Linux
 
* [https://www.youtube.com/watch?v=7LGKgdWtrqI Revolution OS]: documentário sobre Linux
* Apresentação sobre histórico visão geral e estruturas básicas de um SO [[http://wiki.sj.ifsc.edu.br/images/5/58/SOP29005-parte1.pdf slides]]
+
* Apresentação sobre histórico visão geral e estruturas básicas de um SO ([[Arquivo:SOP29005-parte1.pdf]])
 +
* Capítulo 1 do livro do Silberschatz
  
== 14/02: Finalização do conteúdo anterior e atividades em laboratório: Introdução ao Linux e GCC/G++ ==
+
== 06/08: Laboratório: Linux e GCC/G++ ==
 
* Referência sobre C++: http://www.cplusplus.com/
 
* Referência sobre C++: http://www.cplusplus.com/
 
* Livros sobre C++:
 
* Livros sobre C++:
Linha 397: Linha 143:
 
* Tutorial C++
 
* Tutorial C++
 
** [http://www.horstmann.com/ccj2/ccjapp3.html Moving from Java to C++]
 
** [http://www.horstmann.com/ccj2/ccjapp3.html Moving from Java to C++]
* Exercício-exemplo feito em aula. [[https://www.dropbox.com/s/ynqrzb36p0u0r2q/my_first_cpp_program.zip código-fonte]]
+
* Exercício-exemplo feito em aula. [[https://www.dropbox.com/s/nkiw7y4trrbixuy/my_first_cpp_program.zip código-fonte]]
  
== 18/02: Mais sobre desenvolvimento em C++ com Linux – Herança, métodos e atributos estáticos, bibliotecas ==
 
  
 
=== Herança ===
 
=== Herança ===
Linha 433: Linha 178:
 
     int width, height;
 
     int width, height;
 
   public:
 
   public:
 +
 +
    virtual int area() = 0;
 +
 
     void set_values (int a, int b)
 
     void set_values (int a, int b)
 
       { width=a; height=b;}
 
       { width=a; height=b;}
Linha 531: Linha 279:
 
Uma biblioteca é uma coleção de objetos, assim como uma biblioteca tradicional é uma coleção de livros. Quando construindo seu programa, você pode utilizar, no gcc, uma ou mais bibliotecas, de modo que o gcc utilizará os objetos nestas bibliotecas para completar seu programa. Por exemplo, todas as funções da biblioteca padrão C (como ''printf'' e ''exit'') estão em uma biblioteca C, geralmente na pasta lib/libc.a da sua instalação GCC. Quando você faz a ligação do seu programa, o GCC adiciona ao binário os objetos da biblioteca C necessários, baseando-se nas chamadas de funções do seu programa. Importante perceber que apenas as funções/objetos utilizados são ligados ao programa, não gerando desperdício de tempo e espaço.
 
Uma biblioteca é uma coleção de objetos, assim como uma biblioteca tradicional é uma coleção de livros. Quando construindo seu programa, você pode utilizar, no gcc, uma ou mais bibliotecas, de modo que o gcc utilizará os objetos nestas bibliotecas para completar seu programa. Por exemplo, todas as funções da biblioteca padrão C (como ''printf'' e ''exit'') estão em uma biblioteca C, geralmente na pasta lib/libc.a da sua instalação GCC. Quando você faz a ligação do seu programa, o GCC adiciona ao binário os objetos da biblioteca C necessários, baseando-se nas chamadas de funções do seu programa. Importante perceber que apenas as funções/objetos utilizados são ligados ao programa, não gerando desperdício de tempo e espaço.
  
Para fazer usa própria biblioteca, você precisa, primeiro, compilar cada um dos arquivos-fonte, gerando um conjunto de arquivos-objeto. Aqui utilizaremos, como exemplo, o código do [https://www.dropbox.com/s/ynqrzb36p0u0r2q/my_first_cpp_program.zip exercício-exemplo da aula anterior].
+
Para fazer usa própria biblioteca, você precisa, primeiro, compilar cada um dos arquivos-fonte, gerando um conjunto de arquivos-objeto. Aqui utilizaremos, como exemplo, o código do [https://www.dropbox.com/s/nkiw7y4trrbixuy/my_first_cpp_program.zip exercício-exemplo da aula anterior].
  
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
Linha 560: Linha 308:
 
*'''Leitura extra - uso da C++ Standard Template Library:''' http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html
 
*'''Leitura extra - uso da C++ Standard Template Library:''' http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html
  
 +
== 07/08: Arquitetura de sistemas operacionais e modelos de programação ==
  
== 21/02: Finalização do conteúdo anterior e atividades em laboratório ==
+
* Apresentação sobre histórico visão geral e estruturas básicas de um SO ([[Arquivo:SOP29005-parte1.pdf]])
 
+
* Capítulo 2 do livro do Silberschatz
=== Uso básico do Shell ===
 
=> Roteiro do Prof. Maziero: http://dainf.ct.utfpr.edu.br/~maziero/doku.php/unix:shell_basico
 
 
 
=== t0: Biblioteca de Filas ===
 
 
 
Uma maneira de descrever um Sistema Operacional, sob o ponto de vista da programação, é definir-lo como um grande '''gerenciador de filas'''. São exemplos de filas importantes de um SO as filas de processos prontos a serem executados, processos suspensos, processos dormindo e processos bloqueados em semáforos. Para o trabalho da disciplina implementaremos nossas filas através de uma ''lista circular duplamente encadeada'', cuja estrutura pode ser vista na figura abaixo.
 
 
 
[[Arquivo:DoublyCircularlyLinkedList.png]]
 
 
 
Para recapitular a execução de operações sobre uma lista encadeada, consulte o material de [http://wiki.sj.ifsc.edu.br/index.php/Professores:PRG2-2013-2#Detalhamento_das_opera.C3.A7.C3.B5es_da_lista Programação II]. Lembre-se, nossa estrutura de dados é uma Fila, ou seja, elementos são inseridos no final e removidos do início.
 
 
 
Neste projeto você deve construir uma pequena biblioteca que ofereça uma classe ''Queue'' com métodos de inserção e remoção de elementos genéricos. O código-base da classe, que está nos arquivos [https://www.dropbox.com/s/8kuskbojurnrtp9/Queue.h Queue.h] e [https://www.dropbox.com/s/r1nf8sh17omsb38/Queue.cc Queue.cc]. A declaração da classe é apresentada abaixo:
 
 
 
<syntaxhighlight lang=cpp>
 
/*
 
* Queue.h
 
*
 
*  Created on: Feb 21, 2014
 
*      Author: arliones
 
*/
 
 
#ifndef QUEUE_H_
 
#define QUEUE_H_
 
 
namespace BOOOS {
 
 
    class Queue {
 
    public:
 
        Queue();
 
        virtual ~Queue();
 
 
        class Element {
 
        public:
 
            Element() { _prev = 0; _next = 0; }
 
            virtual ~Element() {}
 
 
            Element * prev() { return _prev; }
 
            Element * next() { return _next; }
 
            void prev(Element * p) { _prev = p; }
 
            void next(Element * n) { _next = n; }
 
 
        private:
 
            Element * _prev;
 
            Element * _next;
 
        };
 
 
        Element * head() { return &_head; }
 
 
        int length() { return _length; }
 
 
        void insert(Element * elem);
 
 
        Element * remove();
 
 
    private:
 
        Element _head;
 
        int _length;
 
    };
 
 
}
 
 
#endif /* QUEUE_H_ */
 
</syntaxhighlight>
 
 
 
Esta fila organiza objetos do tipo ''Element*''. A classe ''Element'' deve ser estendida para implementar os detalhes da aplicação, como no exemplo do arquivo [https://www.dropbox.com/s/pgj2yz0ci69uggn/Queue_Test.cc Queue_Test.cc], apresentado abaixo:
 
 
 
<syntaxhighlight lang=cpp>
 
/*
 
* Queue_Test.cc
 
*
 
*  Created on: Feb 21, 2014
 
*      Author: arliones
 
*/
 
 
#include <iostream>
 
#include <Queue.h>
 
 
using namespace std;
 
using namespace BOOOS;
 
 
class MyElement : public Queue::Element {
 
public:
 
    MyElement(string str) : _name(str) {}
 
 
    virtual ~MyElement() {}
 
 
    string & name() { return _name; }
 
 
private:
 
    string _name;
 
};
 
 
 
void print_queue(Queue & q) {
 
    cout << "Queue length: " << q.length() << endl;
 
 
 
    if(q.length()) {
 
        MyElement * elem = dynamic_cast<MyElement *>(q.head()->next());
 
        do {
 
            cout << elem->name() << endl;
 
            elem = dynamic_cast<MyElement *>(elem->next());
 
        } while (elem != q.head()->next());
 
    }
 
  
    cout << "==============================" << endl;
+
== 13/08: Gerência de tarefas; contextos, processos e threads ==
}
 
  
int main() {
+
* Apresentação sobre Gerenciamento de Processos ([[Arquivo:SOP29005-parte2.pdf]])
    cout << "Welcome to BOOOS - Basic Object Oriented Operating System!" << endl;
+
* Capítulo 3 do livro do Silberschatz
    cout << "This program will test the class: Queue" << endl;
 
 
    Queue queue;
 
 
    MyElement * person1 = new MyElement("João");
 
    MyElement * person2 = new MyElement("Pedro");
 
    MyElement * person3 = new MyElement("Augusto");
 
    MyElement * person4 = new MyElement("Fábio");
 
 
    queue.insert(person1);
 
    queue.insert(person2);
 
    queue.insert(person3);
 
    queue.insert(person4);
 
 
    MyElement * removed_person = queue.remove();
 
    delete removed_person; // Which element was removed?
 
 
    return 0;
 
}
 
</syntaxhighlight>
 
 
 
É responsabilidade do aluno implementar mais testes além dos que estão no exemplo para garantir o funcionamento da fila. Apenas o arquivo Queue.cc deve ser entregue ao professor, devidamente preenchido (os métodos estão vazios no original).
 
 
 
Um projeto pré-configurado para o Elicpse Kepler também foi disponibilizado [https://www.dropbox.com/s/npj64iphyqfi64m/booos.zip aqui]. Para utilizar este projeto:
 
# Baixe o [http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/keplersr1 Eclipse Kepler para C/C++];
 
# O eclipse não precisa ser instalado, basta descompactar o arquivo ZIP baixado;
 
# Execute o eclipse através do executável "eclipse" na basta descompactada;
 
# Baixe o projeto disponibilizado pelo professor;
 
# No eclipse, acesse "Arquivo->Importar..." ou "File->Import...";
 
# Selecione a opção "Geral->Projetos Existentes para a Área de Trabalho" ou "General->Existing Projects into Workspace" e clique "Next" ou "Próximo";
 
# Selecione a opção "Selecionar arquivo compactado" ou "Select archive file", clique em "Buscar..." ou "Browse..." e selecione o arquivo zip do projeto;
 
# Verifique se o projeto chamado "booos" está selecionado na lista de "Projetos" ou "Projects";
 
# Clique em "Encerrar" ou "Finish", e o projeto deve aparecer no espaço de trabalho.
 
 
 
* Se você está utilizando o Ubuntu 13.10 e encontrou problemas com os menus, crie um script chamado "run_eclipse.sh" na mesma pasta do executável do eclipse com o conteúdo abaixo e execute este script ao invés do executável. Você pode precisar ajustar as permissões do arquivo executando "chmod +x run_eclipse.sh".
 
<syntaxhighlight lang=bash>
 
#!/bin/bash
 
UBUNTU_MENUPROXY= ./eclipse &
 
</syntaxhighlight>
 
  
 
+
== 14/08: Atividades em laboratório: API POSIX – fork/wait ==
==== Resumo do trabalho ====
 
*'''O que é?''' Classe implementando fila como uma lista circular duplamente encadeada.
 
*'''Duração estimada:''' 4 horas.
 
*'''Dependências:''' Conhecimento básico de C/C++ e de estruturas de dados.
 
*'''Entrega:''' Até 14/03, por email, apenas o arquivo Queue.cc.
 
*'''Observação:''' Este trabalho será base para os demais trabalhos realizados no curso, logo, dediquem-se a esta implementação.
 
 
 
== 25/02: Gerência de tarefas: contextos, processos e threads ==
 
* Apresentação sobre Gerenciamento de Processos. [[http://wiki.sj.ifsc.edu.br/images/a/ae/SOP29005-parte2.pdf slides]]
 
  
 
=== Roteiro de exercícios: gerenciamento de processos ===
 
=== Roteiro de exercícios: gerenciamento de processos ===
Linha 743: Linha 341:
 
** Todas estas syscalls são utilizadas para aguardar por mudanças no estado de um processo filho e obter informações sobre o processo filho cujo estado tenha mudado. São consideradas mudanças de estado: o filho terminou; o filho foi finalizado por um sinal (ex.: kill); o filho foi retomado por um sinal (ex.: alarme);
 
** Todas estas syscalls são utilizadas para aguardar por mudanças no estado de um processo filho e obter informações sobre o processo filho cujo estado tenha mudado. São consideradas mudanças de estado: o filho terminou; o filho foi finalizado por um sinal (ex.: kill); o filho foi retomado por um sinal (ex.: alarme);
 
** A chamada wait também libera os recursos do processo filho que termina;
 
** A chamada wait também libera os recursos do processo filho que termina;
** '1'wait()'1': esta função suspende a execução do processo chamador até que UM DOS SEUS FILHOS finalize;
+
** '''wait()''': esta função suspende a execução do processo chamador até que UM DOS SEUS FILHOS finalize;
 
** '''waitpid()''': suspende a execução do processo chamador até que UM FILHO ESPECÍFICO finalize;
 
** '''waitpid()''': suspende a execução do processo chamador até que UM FILHO ESPECÍFICO finalize;
  
==== Exemplos POSIX utilizando fork/wait ====
+
==== Syscall EXEC====
 +
 
 +
* A syscall EXEC é implementada no POSIX pela família de funções '''exec()'''. Execute "man exec".
 +
** As principais funções da família são '''execl()''', '''execlp()''' e '''execvp()''';
 +
** Todas estas funções são, na realidade, front-ends (abstrações) para a syscall '''execve'''. Esta syscall substitui a imagem do processo corrente (aquele que chama a syscall) pela a imagem de um novo processo;
 +
** Os parâmetros passados a estas funções são, basicamente, o nome de um arquivo com a imagem do programa a ser executado (um binário de um programa), e uma lista de parâmetros a serem passados a este novo programa;
 +
 
 +
 
 +
==== Exemplos POSIX utilizando fork/wait/exec ====
  
 
*Exemplo 1: fork/wait básico
 
*Exemplo 1: fork/wait básico
Linha 754: Linha 360:
 
#include <stdlib.h>
 
#include <stdlib.h>
 
#include <stdio.h>
 
#include <stdio.h>
 +
#include <unistd.h>
  
 
int main()
 
int main()
Linha 768: Linha 375:
 
     {
 
     {
 
         printf("processo filho\t pid: %d\t pid pai: %d\n", getpid(), getppid());
 
         printf("processo filho\t pid: %d\t pid pai: %d\n", getpid(), getppid());
 +
        execl("/bin/ls","ls", (char*) NULL);
 +
        perror("execl falhou!");
 
         exit(0);
 
         exit(0);
 
     }
 
     }
Linha 858: Linha 467:
 
Excrever um programa C que cria uma árvore de 3 processos, onde o processo A faz um ''fork()'' criando um processo B, o processo B, por sua vez, faz um ''fork()'' criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY são PIDs de processos. Utilizar ''wait()'' para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar ''sleep()'' (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.
 
Excrever um programa C que cria uma árvore de 3 processos, onde o processo A faz um ''fork()'' criando um processo B, o processo B, por sua vez, faz um ''fork()'' criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY são PIDs de processos. Utilizar ''wait()'' para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar ''sleep()'' (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.
  
== 28/02: Finalização do conteúdo anterior e atividades em laboratório ==
 
* Aviso: mudança na data de viagem do professor - aulas dos dias 25 e 28 de março ocorrerão normalmente.
 
* Revisão da solução do exercício fork/wait (aula anterior).
 
  
===t1: Troca de contexto em nível de usuário===
+
== 20/08: Escalonamento de tarefas ==
 +
 
 +
* Notas de aula ([[Arquivo:SOP29005-parte2.pdf]])
 +
* [http://courses.cs.vt.edu/csonline/OS/Lessons/Processes/index.html Animação de escalonamento de processos - Virginia Tech]
 +
* Capítulo 5 do livro do Silberschatz.
 +
 
 +
== 21/08: Atividades em laboratório: Estrutura de processos ==
 +
 
 +
* [http://www.lisha.ufsc.br/teaching/os/exercise/hello.html A verdadeira hitória do "Hello World!"]
 +
 
  
:O Linux, através da API POSIX, oferece um conjunto de funções que permite às aplicações manipular contextos, facilitando a vida do programador que quer implementar tarefas "simultâneas" dentro de um único processo, ou seja, threads. As seguintes funções e tipos estão disponíveis:
+
=== Troca de contexto em nível de usuário ===
 +
 
 +
O Linux, através da API POSIX, oferece um conjunto de funções que permite às aplicações manipular contextos, facilitando a vida do programador que quer implementar tarefas "simultâneas" dentro de um único processo, ou seja, threads. As seguintes funções e tipos estão disponíveis:
 
*'''getcontext(&a)''': salva o contexto na variável '''a''';
 
*'''getcontext(&a)''': salva o contexto na variável '''a''';
 
*'''setcontext(&a)''': restaura um contexto salvo anteriormente na variável '''a''';
 
*'''setcontext(&a)''': restaura um contexto salvo anteriormente na variável '''a''';
Linha 871: Linha 488:
 
*'''ucontext_t''':  as variáveis '''a''' e '''b''' são do tipo '''ucontext_t'''. Este tipo armazena um contexto.
 
*'''ucontext_t''':  as variáveis '''a''' e '''b''' são do tipo '''ucontext_t'''. Este tipo armazena um contexto.
  
:Busque mais informações sobre estas funções utilizando o programa manpage do Linux (ex.: man getcontext).
+
Busque mais informações sobre estas funções utilizando o programa manpage do Linux (ex.: man getcontext).
 
 
:O objetivo deste trabalho é familiarizar o aluno com o conjunto de funções de manipulação de contexto de execução em espaço de usuário. Estas mesmas funções serão utilizadas no t2 para implementar uma classe de tarefa para nosso projeto.
 
  
:O entregável deste trabalho é um relatório. Estude o código no arquivo pingpong.c abaixo e explique seu funcionamento.
+
Estude o código no arquivo pingpong.c abaixo e explique seu funcionamento.
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
Linha 960: Linha 575:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
:Elabore o relatório cobrindo/explicando, ao menos, o seguinte:
+
== 27/08: Atividades em laboratório: acompanhamento de projetos ==
#Objetivo e parâmetros de cada uma das funções;
 
#Significado dos campos do tipo (struct) '''ucontext_t''' que foram utilizados no código acima;
 
#Explique o objetivo de cada linha de código de pingpong.c que chama uma destas funções ou que manipule ucontext_t.
 
  
==== Resumo do trabalho ====
+
== 28/08: Algoritmos de escalonamento ==
*'''O que é?''' Estudo sobre funções de manipulação de contexto em espaço de usuário no Linux.
 
*'''Duração estimada:''' 2 horas.
 
*'''Dependências:''' Conhecimento básico de C e aula inicial de gerenciamento de processos.
 
*'''Entrega:''' Até 21/03, por email, apenas arquivo do relatório EM PDF.
 
  
 +
* Notas de aula ([[Arquivo:SOP29005-parte2.pdf]])
 +
* Capítulo 5 do livro do Silberschatz.
  
===t2: Estruturas de tarefas===
 
  
:Neste trabalho deve-se extender o projeto sendo desenvolvido no curso com a construção de uma classe para abstrair processos em nível de usuários - na prática, threads. A classe implementada será chamada de '''Task''' (tarefa). Lembre-se que uma classe é uma estrutura de dados, logo, nossa classe '''Task''' será o PCB ('''Proccess Control Block''') do sistema. A partir deste trabalho, será disponibilizado um gabarito em C++ como no código abaixo, geralmente incompleto, e um diagrama UML de uma versão completa da solução implementada pelo professor, como na imagem abaixo.
+
== 03/09: Algoritmos de escalonamento / Revisão para prova ==
  
[[Arquivo:Booos.png|600px]]
+
== 04/09: Listas de exercícios / Revisão para prova ==
  
<syntaxhighlight lang=c>
+
== 10/09: Prova 0 - Processos ==
/*
 
* Task.h
 
*
 
*  Created on: Feb 27, 2014
 
*      Author: arliones
 
*/
 
  
#ifndef TASK_H_
+
== 11/09: Atividades em laboratório: pthreads – t2: escalonamento FIFO e por prioridades ==
#define TASK_H_
 
  
#include <Queue.h>
+
* Conclusão do conteúdo da aula anterior
#include <ucontext.h>
 
  
namespace BOOOS {
+
=== Introdução a POSIX Threads ===
  
class Task : public Queue::Element {
+
A biblioteca POSIX Threads, ou simplesmente ''pthreads'', é parte do padrão POSIX para programar utilizando threads. O padrão POSIX.1c define a API para criação e manipulação de threads. Esta API é encontrada na maioria dos sistemas baseados no Unix, como Linux, Mac OS X, Solaris, entre outros. Também existem alternativas adaptando a API para sistemas Windows, como a pthreads-w32.
public:
 
enum State {
 
READY,
 
WAITING,
 
RUNNING,
 
FINISHING
 
};
 
  
Task(void (*entry_point)(void *), int nargs, void * arg);
+
A API da pthreads inclui métodos para criar, manipular e destruir threads, além de outras estruturas de dados para sincronizar as threads, incluindo implementações de mutexes e variáveis de condição.
virtual ~Task();
 
  
int tid() { return _tid; }
+
A API POSIX ''semaphore'', utilizada para sincronização de processos ou threads, também funciona em conjunto com a ''pthreads'', embora sua implementação esteja definida em outro padrão, o POSIX.1b.
State state() { return _state; }
 
  
void pass_to(Task * t, State s = READY);
+
Programas em C/C++ que utilizarão a ''pthreads'' devem incluir o cabeçalho ''pthread.h'':
  
void exit(int code);
+
<syntaxhighlight lang=c>
 
+
#include <pthread.h>
static Task * self() { return (Task*)__running; }
 
static void init();
 
 
 
private:
 
static volatile Task * __running;
 
 
 
State _state;
 
int _tid; // task ID
 
// ...
 
};
 
 
 
} /* namespace BOOOS */
 
 
 
#endif /* TASK_H_ */
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
:Os métodos de interface que precisam ser implementados na classe estão na declaração acima. Contudo, observe que você certamente precisará de novos atributos para o correto funcionamento da classe, e seria bom utilizar alguns métodos privados para auxiliar na implementação. Um dos atributos necessários, não declarados, é uma Task guardando o contexto da Main. ''Onde será que será declarado o ucontext_t de cada Task?'' O teste no arquivo test/Task_Test.cc implementa a mesma aplicação do t1 utilizando a Task. Use este exemplo como teste inicial.
 
  
:'''Task(void (*entry_point)(void *), int nargs, void * arg)''': construtor - deve inicializar todos os atributos dos '''objetos'''.
+
==== Principais elementos da API ====
  
:'''virtual ~Task()''': destrutor - deve liberar recursos alocados pelo construtor (new/malloc).
+
* pthread_t (struct)
 +
:Estrutura que armazena dados/atributos de uma pthread.
  
:'''int tid()''': ''getter'' do Task ID (_tid).
+
* pthread_create
 +
: Função que cria uma thread, incializando-a e deixando-a pronta para executar. O código abaixo apresenta um exemplo simples de um programa utilizando pthreads.
  
:'''State state()''': ''getter'' do estado do processo (_state).
+
<syntaxhighlight lang=c>
  
:'''void pass_to(Task * t, State s = READY)''': este método salva o contexto do objeto (this) e carrega o contexto da Task recebida por parâmetro. O estado passado em ''s'' é o novo estado do objeto que está deixando a CPU. ''Há algum atributo de classe (static) que precisa ser atualizado aqui?''
+
#include <pthread.h>
  
:'''void exit(int code)''': finaliza a Task (this) e configura o valor de resultado da task com o valor de ''code''. Por enquanto, se preocupem em fazer a Task retornar a execução para a main. Ignorem o parâmetro ''code'' agora - utilizaremos ele mais adiante.
+
pthread_t threads[2];
  
:'''static Task * self()''': método de classe (static) que retorna a Task executando no momento.
+
void *thread_func(void *arg) {
 +
    // ...
 +
}
  
:'''static void init()''': método de classe que precisa ser chamando na inicialização do sistema e deve inicializar os atributos de classe (static).
+
int main(int argc, char **argv) {
 +
    int i;
 +
    for(i = 0; i < 2; i++) {
 +
        pthread_create(&(threads[i]), NULL, thread_func,NULL);
 +
    }
 +
    for(i = 0; i < 2; i++) {
 +
        pthread_join(threads[i], NULL);
 +
    }
 +
}
  
Os arquivos iniciais do trabalho estão no [https://www.dropbox.com/s/npj64iphyqfi64m/booos.zip zip do projeto]c.
+
</syntaxhighlight>
  
==== Resumo do trabalho ====
 
*'''O que é?''' Classe Task para nosso SO.
 
*'''Duração estimada:''' 4 horas.
 
*'''Dependências:''' t1 (troca de contexto).
 
*'''Entrega:''' Até 21/03, por email, apenas arquivos Task.h, Task.cc e Task_Test.cc.
 
  
== 07/03: Escalonamento de tarefas ==
+
* pthread_join
* Notas de aula [[http://wiki.sj.ifsc.edu.br/images/a/ae/SOP29005-parte2.pdf slides]]
+
: Bloqueia execução de uma thread até que outra thread termine. Similar à chamada de sistema ''wait'' usada em processos Unix.
* [http://courses.cs.vt.edu/csonline/OS/Lessons/Processes/index.html Animação de escalonamento de processos - Virginia Tech]
 
  
== 11/03: Finalização do conteúdo anterior e atividades em laboratório: acompanhamento de projetos ==
+
* pthread_exit
* Finalização do conteúdo de escalonamento de tarefas (aula anterior)
+
: Encerra a execução de uma thread. A chamada a esta função por uma thread gera a liberação de outras threads que estejam, eventualmente, bloqueadas nela por uma chamada ''pthread_join''.
* Acompanhamento de projetos
 
* Lembrete: entrega do t0 (biblioteca de fila) para próxima aula (sexta - 14/03)
 
  
=== t3: Despachante de tarefas ===
+
O código abaixo apresenta um programa completo utilizando pthreads, onde threads recebem argumentos.
Você irá construir um despachante de tarefas baseado em duas entidades: uma tarefa ''dispatcher'', responsável pelo controle geral, e uma função ''choose_next'', responsável por determinar qual a próxima tarefa a executar a cada troca de contexto. A figura abaixo ilustra o funcionamento geral do sistema (fonte Prof. Maziero/UTFPR):
 
  
[[Arquivo:Dispatcher.png]]
+
<syntaxhighlight lang=c>
  
Para isto, uma nova classe será adicionada ao nosso sistema: ''Scheduler''. Como nosso escalonador é também um processo, ele herda de Task. Uma função estática chamada ''dispatcher'' implementa o comportamento de escalonamento.
+
#include <stdlib.h>
 +
#include <stdio.h>
 +
#include <pthread.h>
  
[[Arquivo:Booos-t3.png|600px]]
+
typedef struct {
 +
    int idx, length;
 +
} thread_arg, *ptr_thread_arg;
  
Abaixo, o esqueleto C++ da classe ''Scheduler'':
+
pthread_t threads[2];
<syntaxhighlight lang=cpp>
 
/*
 
* Scheduler.h
 
*
 
*  Created on: Mar 21, 2014
 
*      Author: arliones
 
*/
 
  
#ifndef SCHEDULER_H_
+
void *thread_func(void *arg) {
#define SCHEDULER_H_
+
    ptr_thread_arg targ = (ptr_thread_arg) arg;
 +
    int i;
  
#include <Task.h>
+
    for(i = targ->idx; i < (targ->idx + targ->length); i++)
#include <Queue.h>
+
        printf(“Thread %d – value %d\n”, pthread_self(), i);
 +
}
  
namespace BOOOS {
+
int main(int argc, char **argv) {
 +
    thread_arg arguments[2];
 +
    int i;
 +
    for(i = 0; i < 2; i++) {
 +
        arguments[i].idx = i * 10;
 +
        arguments[i].length = 10;
 +
        pthread_create(&(threads[i]), NULL, thread_func, &(arguments[i]));
 +
    }
 +
    for(i = 0; i < 2; i++) {
 +
        pthread_join(threads[i], NULL);
 +
    }
 +
}
  
class Scheduler : public Task {
+
</syntaxhighlight>
friend class Task;
 
  
protected:
+
Ao compilar um programa com pthreads é necessário "linkar" com a biblioteca. Para isso, deve ser usando a opção -lpthread com o gcc.
Scheduler();
 
  
public:
+
<syntaxhighlight lang=bash>
enum SchedulerType {
+
gcc ... -lpthread
SCHED_FCFS,
 
SCHED_PRIORITY
 
};
 
 
 
virtual ~Scheduler();
 
 
 
static void init();
 
 
 
static void dispatcher(void*);
 
 
 
static Scheduler * self() { return __dispatcher; }
 
 
 
protected:
 
virtual Task * choose_next();
 
 
 
static Scheduler * __dispatcher;
 
};
 
 
 
} /* namespace BOOOS */
 
 
 
#endif /* SCHEDULER_H_ */
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Neste trabalho, os seguintes métodos precisam ser implementados:
+
==== Exercícios ====
*''Scheduler()'': Um construtor que inicializa um Scheduler utilizando o construtor da Task, passando a função ''dispatcher'' como entry point. Importante aqui verificar que um Scheduler é um tipo especial de tarefa: ela está sempre pronta (''ready''), nunca fica bloqueada. '''Dica:''' Para permitir esta identificação, crie um estado extra em Task.h, chamado SCHEDULER, e utilize este estado para diferenciar a Scheduler de uma tarefa normal quando utilizando o ''pass_to'';
 
*''init()'': ele precisa inicializar a tarefa __dispatcher;
 
*''dispatcher(void*)'': esta função implementa o comportamento do escalonador. O pseudo-código abaixo apresenta o comportamento do ''__dispatcher'' de modo simplificado:
 
<syntaxhighlight lang=c>
 
void dispatcher()
 
{
 
    while(userTasks > 0)
 
    {
 
      next = choose_next() ;  // escolher a próxima Task* a executar
 
      if(next)
 
      {
 
          ... // ações antes de lancar a tarefa "next", se houverem
 
          self()->pass_to(next); // transfere controle para a tarefa "next"
 
          ... // ações apos retornar da tarefa "next", se houverem
 
      }
 
    }
 
    exit(0) ; // encerra a tarefa dispatcher
 
}
 
</syntaxhighlight>
 
 
 
*''choose_next()'': este método '''virtual''' é o responsável por implementar a política de escalonamento empregada. Neste trabalho utilizaremos uma política FCFS (''First-Come First-Served''). '''Dica:''' pense na relação entre esta política e a fila que implementamos - esta função deve ser extremamente simples!
 
 
 
Uma aplicação de testes está disponível junto dos esqueletos do projeto [https://www.dropbox.com/s/npj64iphyqfi64m/booos.zip aqui]. Algumas outras observações:
 
* Como nosso sistema está ficando mais complexo, foi criado um arquivo geral de configuração para ele: o lib/BOOOS.h. Por enquanto, ele tem uma função ''init'' que chamará os ''inits'' dos outros componentes. Ele também tem um parâmetro de configuração do sistema: o tipo de escalonador utilizado.
 
* O arquivo Scheduler.h disponibilizado está completo, ou seja, você não precisa modificá-lo, a não ser que queira.
 
* Serão necessárias algumas mudanças na classe Task:
 
** Novo estado: SCHEDULER
 
** Novo método ''yield()'' que transfere a execução da tarefa corrente para o escalonador. '''Dica:''' utilize o ''pass_to''.
 
** A fila de tarefas prontas (_ready) deve ser um atributo de classe (static) de ''Task''. Tasks devem ser incluídas na fila quando criadas e removidas quando destruídas.
 
** A classe Scheduler é ''friend'' da classe Task. Isto significa que Scheduler pode manipular os atributos protegidos de Task.
 
 
 
==== Resumo do trabalho ====
 
*'''O que é?''' Classe implementando um escalonador FCFS para nosso sistema.
 
*'''Duração estimada:''' 5 horas.
 
*'''Dependências:''' Queue e Task.
 
*'''Entrega:''' Até 07/04, por email.
 
  
 +
* Implemente o exemplo do Ping e Pong utilizando pthreads.
  
=== t4: Escalonamento por prioridades ===
 
  
No t3 foi implementado um escalonador '''First-Come First-Served''' (FCFS). Agora, a política de escalonamento deve ser modificada para escalonar tarefas por prioridades. O diagrama abaixo apresenta as modificações necessárias para que o sistema escalone por prioridades.
 
  
[[Arquivo:Booos-prio.png|600px]]
+
== 17/09 e 18/09: Comunicação entre processos ==
 
+
* Notas de aula ([[Arquivo:SOP29005-parte3.pdf]])
# ''Queue::Element::_rank'': nosso elemento terá um rank genérico pelo qual a fila pode ser mantida em ordem. O valor padrão deste atributo é zero. O atributo é privado e deve ter métodos getter e setter (''rank()'' e ''rank(int r)'');
 
# ''Queue::insert_ordered(Element* elem)'': nossa fila agora deve ter um método que a mantém ordenada pelo valor do ''_rank'' de cada elemento;
 
# ''Task::nice(int p)'': nossa Task terá um método chamado ''nice'' que configura a prioridade da tarefa ajustando o valor de seu ''_rank''. O escalonador ''DEVE'' utilizar prioridades no estilo UNIX, ou seja, com valores entre -20 e 20. ''Curiosidade:'' abra um terminal e digite '''man nice''';
 
# ''SCHEDULER_TYPE'': parâmetro de configuração do sistema que define o tipo escalonador utilizado. ''Dica:'' utilize este parâmetro para decidir se o sistema de utilizar ''Queue::insert'' ou ''Queue::insert_ordered''.
 
 
 
==== Resumo do trabalho ====
 
*'''O que é?''' Modificações no sistema para suporta escalonamento por prioridade.
 
*'''Duração estimada:''' 3 horas.
 
*'''Dependências:''' Queue, Task e Scheduler.
 
*'''Entrega:''' Até 11/04, por email.
 
 
 
== 14/03: Atividades em laboratório: acompanhamento de projetos ==
 
* Acompanhamento de projetos
 
* Lembrete: entrega do t0 (biblioteca de fila) para hoje (sexta - 14/03)
 
 
 
== 17/03: Comunicação entre processos ==
 
* Lembrete: entrega de t1 e t2 (troca de contexto em nível de usuário) para sexta – 21/03.
 
* Notas de aula [[http://wiki.sj.ifsc.edu.br/images/b/b7/SOP29005-parte3.pdf slides]]
 
  
 
===Comunicação entre processos: Troca de mensagens===
 
===Comunicação entre processos: Troca de mensagens===
Linha 1 218: Linha 730:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
*'''Desafio: construa um “pipeline”'''. Crie um programa que conecta 4 processos através de 3 pipes, assim como na figura abaixo. Utilize ''fork()'' para criar vários processos.
+
*'''Desafio 1: construa um “pipeline”'''. Crie um programa que conecta 4 processos através de 3 pipes. Utilize ''fork()'' para criar vários processos.
 +
 
 +
*'''Desafio 2: cópia de arquivo'''. Projete um programa de cópia de arquivos chamado FileCopy usando pipes comuns. Esse programa receberá dois parâmetros: o primeiro é o nome do arquivo a ser copiado e o segundo é o nome do arquivo copiado. Em seguida, o programa criará um pipe comum e gravará nele o conteúdo do arquivo a ser copiado. O processo filho lerá esse arquivo do pipe e o gravará no arquivo de destino. Por exemplo, se chamarmos o programa como descrito a seguir:
 +
:<syntaxhighlight lang=bash>
 +
$ FileCopy entrada.txt copia.txt
 +
</syntaxhighlight>
 +
:o arquivo ''entrada.txt'' será gravado no pipe. O processo filho lerá o conteúdo desse arquivo e o gravará no arquivo de destino ''copia.txt''. Escreva o programa usando os pipes da API POSIX no Linux.
  
== 21/03: Comunicação entre processos ==
 
* Lembrete: entrega de t1 e t2 (troca de contexto em nível de usuário) para hoje.
 
  
 
===Comunicação entre processos: Memória Compartilhada===
 
===Comunicação entre processos: Memória Compartilhada===
Linha 1 328: Linha 844:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
*'''Desafio: destrua um shm.''' Crie um programa que destrua o shm utilizado nos programas anteriores. Para isso utilize ''shmctl'' com o parâmetro apropriado (veja "man shmctl").
+
*'''Desafio1: destrua um shm.''' Crie um programa que destrua o shm utilizado nos programas anteriores. Para isso utilize ''shmctl'' com o parâmetro apropriado (veja "man shmctl").
  
 
<!--
 
<!--
Linha 1 371: Linha 887:
 
-->
 
-->
  
== 24/03: Coordenação entre processos ==
+
== 24/09: Coordenação de processos ==
  
* Sincronização entre processos [[http://wiki.sj.ifsc.edu.br/images/b/b7/SOP29005-parte3.pdf slides]]
+
* Sincronização entre processos ([[Arquivo:SOP29005-parte4.pdf]])
 
** Algoritmos de sincronização
 
** Algoritmos de sincronização
 +
** Exclusão Mútua
 
** Semáforos
 
** Semáforos
  
== 28/03: Impasses e Problemas clássicos de coordenação ==
+
*Curiosidade: [http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/authoritative_account.html A inversão de prioridades na Mars Pathfinder]
 +
 
 +
 
 +
== 25/09: Laboratório: Impasses e Problemas clássicos de coordenação ==
 +
 
 +
* Sincronização entre processos ([[Arquivo:SOP29005-parte4.pdf]])
 +
 
 +
=== POSIX pthread mutex ===
 +
 
 +
A biblioteca pthread implementa um tipo ''pthread_mutex_t'', que garante a exclusão mútua entre threads. Estes mutex são manipulados através das funções (acesse as man-pages das chamadas para maiores detalhes):
 +
*''pthread_mutex_lock'': acessa um mutex.
 +
*''pthread_mutex_trylock'': tenta acessar um mutex (retorna valor indicando sucesso ou falha no lock).
 +
*''pthread_mutex_unlock'': libera um mutex.
 +
 
 +
<syntaxhighlight lang=c>
 +
#ifndef __mutex_h
 +
#define __mutex_h
 +
 
 +
#include <pthread.h>
 +
 
 +
class Mutex
 +
{
 +
public:
 +
    Mutex() {}
 +
    ~Mutex() {}
 +
 
 +
    void lock() { pthread_mutex_lock(&mut); }
 +
    bool try_lock() { return (pthread_mutex_trylock(&mut) == 0); } // true when succeeds.
 +
    void unlock() { pthread_mutex_unlock(&mut); }
 +
 
 +
private:
 +
    pthread_mutex_t mut;
 +
};
 +
 
 +
#endif
  
* Sincronização entre processos [[http://wiki.sj.ifsc.edu.br/images/b/b7/SOP29005-parte3.pdf slides]]
+
</syntaxhighlight>
** Impasses e tratamento de impasses
 
  
 
=== POSIX Semaphores ===
 
=== POSIX Semaphores ===
Linha 1 386: Linha 936:
 
Nos sistemas POSIX, semáforos são implementados pelo tipo ''sem_t'' e manipulado através das funções (acesse as man-pages das chamadas para maiores detalhes):
 
Nos sistemas POSIX, semáforos são implementados pelo tipo ''sem_t'' e manipulado através das funções (acesse as man-pages das chamadas para maiores detalhes):
 
*''sem_init'': inicializa um semáforo;
 
*''sem_init'': inicializa um semáforo;
*''sem_destroy'': destroty um semáforo;
+
*''sem_destroy'': destroy um semáforo;
 
*''sem_wait'': implementa a operação ''p'';
 
*''sem_wait'': implementa a operação ''p'';
 
*''sem_post'': implementa a operação ''v''.
 
*''sem_post'': implementa a operação ''v''.
Linha 1 404: Linha 954:
 
     void p() { sem_wait(&sem); }
 
     void p() { sem_wait(&sem); }
 
     void v() { sem_post(&sem); }
 
     void v() { sem_post(&sem); }
 +
 +
    operator int()
 +
    {
 +
        int ret;
 +
        sem_getvalue(&sem, &ret);
 +
        return ret;
 +
    }
  
 
private:
 
private:
Linha 1 412: Linha 969:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
Exemplo de uso do operator:
 +
<syntaxhighlight lang=cpp>
 +
Semaphore sem;
 +
cout << (int)sem << endl;
 +
</syntaxhighlight>
  
 
=== POSIX Threads ===
 
=== POSIX Threads ===
Linha 1 451: Linha 1 013:
 
O problema clássico Produtor/Consumidor consiste em dois fluxos de execução (threads/processos), sendo que um dos fluxos (consumidor) só pode executar a partir do momento em que seus dados de entrada foram produzidos pelo outro fluxo (produtor).
 
O problema clássico Produtor/Consumidor consiste em dois fluxos de execução (threads/processos), sendo que um dos fluxos (consumidor) só pode executar a partir do momento em que seus dados de entrada foram produzidos pelo outro fluxo (produtor).
 
*[http://cs.uttyler.edu/Faculty/Rainwater/COSC3355/Animations/processsync.htm Veja esta simulação]
 
*[http://cs.uttyler.edu/Faculty/Rainwater/COSC3355/Animations/processsync.htm Veja esta simulação]
 +
*[http://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem Veja esta descrição do problema]
  
 
*''DESAFIO'': O programa abaixo implementa um produtor/consumidor utilizando semáforos para sincronização. Contudo, as chamadas para as operações ''v'' e ''p'' foram removidas, conforme comentários no código. Corrija este programa.
 
*''DESAFIO'': O programa abaixo implementa um produtor/consumidor utilizando semáforos para sincronização. Contudo, as chamadas para as operações ''v'' e ''p'' foram removidas, conforme comentários no código. Corrija este programa.
Linha 1 526: Linha 1 089:
  
 
==== Jantar dos Filósofos ====
 
==== Jantar dos Filósofos ====
O problema clássico Jantar dos Filósofos consiste em quem n fluxos (n filósofos) disputam n recursos (n talheres). No problema, para conseguir "jantar" (ou executar), cada filósofo precisa pegar dois talheres adjascentes a ele. Cada recurso é compartilhado por dois filósofos.
+
O problema clássico Jantar dos Filósofos consiste em que n fluxos (n filósofos) disputam n recursos (n talheres). No problema, para conseguir "jantar" (ou executar), cada filósofo precisa pegar dois talheres adjascentes a ele. Cada recurso é compartilhado por dois filósofos.
 
*[http://www.doc.ic.ac.uk/~jnm/concurrency/classes/Diners/Diners.html Veja esta simulação]
 
*[http://www.doc.ic.ac.uk/~jnm/concurrency/classes/Diners/Diners.html Veja esta simulação]
 +
*[http://en.wikipedia.org/wiki/Dining_philosophers_problem Veja esta descrição do problema]
  
 
*''DESAFIO'': O programa abaixo implementa um Jantar dos Filósofos utilizando semáforos para sincronização. Contudo, as chamadas para as operações ''v'' e ''p'' foram removidas, conforme comentários no código. Corrija este programa.
 
*''DESAFIO'': O programa abaixo implementa um Jantar dos Filósofos utilizando semáforos para sincronização. Contudo, as chamadas para as operações ''v'' e ''p'' foram removidas, conforme comentários no código. Corrija este programa.
Linha 1 587: Linha 1 151:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== 31/03: Acompanhamento de projetos e revisão para prova ==
 
  
* Nossa prova será na sexta (04/04), na sala 09.
+
== 01/10 a 08/10: Laboratório: programação concorrente ==
  
 +
=== Exercício 1 ===
  
=== t5: Preempção e compartilhamento de tempo ===
+
O programa abaixo cria 5 threads, e cada uma destas threads atualiza uma variável global (memória compartilhada).
  
Até este ponto nosso sistema apenas escalona tarefas de modo cooperativo, ou seja, para que a CPU passe de uma tarefa para outra é necessário que a tarefa em execução chame explicitamente ''pass_to'' ou ''yield''. Neste trabalho, adicionaremos suporte a preempção ao BOOOS.
+
<syntaxhighlight lang=cpp>
 +
#include <iostream>
 +
#include "thread.h"
  
==== Preempção ====
+
#define NUM_THREADS 5
*Baseado em texto do Prof. Maziero (UTFPR).
 
  
Em sistemas de compartilhamento de tempo ('''time-sharing''') as tarefas recebem "fatias de tempo" de CPU para utilizar. Estas fatias, chamadas de '''quantum''', variam tipicamente entre 1 ms e 100 ms. Ao final de um '''quantum''', a tarefa em execução retorna à fila de prontas e a CPU é passada à próxima tarefa.
+
using namespace std;
  
Em sistemas reais o controle do '''quantum''' pelo sistema operacional é realizada por um dispositivo de hardware chamado temporizador ('''Timer'''). O dispositivo é programado para gerar interrupções a cada 1 ms, que são tratadas por um '''tratador de interrupção''' ('''interrupt handler'''). Essas ativações periódicas do tratador de interrupção são chamadas de '''ticks''' do relógio.
+
int saldo = 1000;
  
Quando uma tarefa recebe o processador, o '''dispatcher''' ajusta um contador de '''ticks''' que essa tarefa pode usar, ou seja, seu quantum é definido em número de '''ticks'''. A cada '''tick''', esse contador deve ser verificado e quando o quantum for atingido o processador deve ser devolvido ao '''dispatcher''' para que ele realize um escalonamento.
+
int AtualizaSaldo(int n)
 
 
Como um processo UNIX não tem acesso direto aos temporizadores e interrupções do hardware, vamos simular o temporizador de hardware por um temporizador UNIX, e o mecanismo de interrupção através de '''sinais UNIX''', que serão explicados a seguir.
 
 
 
==== Sinais UNIX ====
 
*Baseado em texto do Prof. Maziero (UTFPR).
 
 
 
O mecanismo de sinais do UNIX é ''similar'' às interrupções (IRQs) geradas pelo hardware: ao receber um sinal, um processo desvia sua execução para uma função que ele previamente registrou no sistema operacional.
 
 
 
A página de manual signal (seção 7 - '''man 7 signal''') relaciona os principais sinais disponíveis em um sistema UNIX e as ações que cada sinal pode desencadear no processo que o recebe. Através da chamada de sistema '''sigaction''' ('''man sigaction''') é possível registrar uma função de tratamento para um determinado sinal ('''signal handler function''').
 
 
 
Um exemplo de uso de sinais está no código abaixo. Nele, uma função é registrada para tratar o sinal SIGINT, que corresponde ao Control-C do teclado. Analise atentamente seu código, execute-o e observe seu comportamento.
 
 
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <signal.h>
 
 
 
/* função que tratará os sinais recebidos */
 
void tratador (int signum)
 
 
{
 
{
  printf ("Recebi o sinal %d\n", signum) ;
+
int meu_saldo = saldo;
 +
int novo_saldo = meu_saldo + n*100;
 +
cout << "Novo saldo = " << novo_saldo << endl;
 +
saldo = novo_saldo;
 
}
 
}
  
int main (void)
+
int main()
 
{
 
{
  struct sigaction action;
+
Thread * threads[NUM_THREADS];
  
  /* Configura a estrutura que especifica a nova ação */
+
for(int t = 0; t < NUM_THREADS; t++)
  action.sa_handler = tratador;
+
threads[t] = new Thread(&AtualizaSaldo, t+1);
  sigemptyset (&action.sa_mask);
 
  action.sa_flags = 0;
 
  
  /* registra ação para o sinal SIGINT (^C do teclado) */
+
int status;
  if (sigaction (SIGINT, &action, 0) < 0)
+
for(int t = 0; t < NUM_THREADS; t++) {
  {
+
threads[t]->join(&status);
      perror ("Erro em sigaction: ") ;
+
cout << "Thread " << t << " terminou com status " << status << "." << endl;
      exit (1) ;
+
}
  }
 
  
  /* laço vazio */
+
cout << "Saldo final é " << saldo << "." << endl;
  while (1) ;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== Temporizadores UNIX ====
 
  
Para simular as interrupções de relógio do hardware, faremos uso do mecanismo de sinais (para implementar a preempção) e de temporizadores UNIX (para gerar os ticks de relógio). O UNIX permite definir temporizadores através das chamadas de sistemas '''getitimer''' e '''setitimer'''. Ao disparar, um temporizador gera um sinal para o processo, que pode ser capturado por uma função tratadora previamente registrada por ele. O código abaixo mostra um exemplo:
+
# Compile este programa. Você precisará da classe Thread implementada na aula passada.
 +
# Execute este programa várias vezes. Ele funciona? Será que ele gera as saídas esperadas?
 +
# Identifique as '''seções críticas''' do programa.
 +
# Corrija o programa utilizando '''mutex'''. Utilize a classe Mutex implementada na aula passada.
 +
# Analise a função ''AtualizaSaldo()'' com a sua solução. Lembre-se que o uso do mutex implica em apenas uma thread acessar a seção crítica por vez, enquanto outras threads ficam bloqueadas, esperando. Disso vem que, quanto menor o trecho de código entre um ''lock'' e um ''unlock'', menos tempo uma thread necessita ficar esperando.
 +
# Modifique a função ''AtualizaSaldo()'' para reduzir o tempo de lock do programa.
 +
# Modifique o programa para usar um semáforo binário ao invés de um mutex em sua solução. Utilize a classe Semaphore da aula passada.
  
<syntaxhighlight lang=c>
+
=== Exercício 2 ===
#include <stdio.h>
 
#include <stdlib.h>
 
#include <signal.h>
 
#include <sys/time.h>
 
 
 
// tratador do sinal
 
void tratador (int signum)
 
{
 
  printf ("Recebi o sinal %d\n", signum) ;
 
}
 
 
 
int main ()
 
{
 
  struct itimerval timer;
 
  struct sigaction action ;
 
 
 
  // define a ação para o sinal de timer
 
  action.sa_handler = tratador;
 
  sigemptyset (&action.sa_mask);
 
  action.sa_flags = 0;
 
  if (sigaction (SIGALRM, &action, 0) < 0)
 
  {
 
      perror ("Erro em sigaction: ") ;
 
      exit (1) ;
 
  }
 
  
  // ajusta valores do temporizador
+
O programa abaixo manipula uma matriz de tamanho MxN (veja os defines para o tamanho da matriz). A função ''SumValues'' soma todos os valores em uma linha da matriz. A linha a ser somada é identificada pela variável ''i''. Modifique o programa principal (''main'') nos locais indicados para:
  timer.it_value.tv_usec = 0 ;      // primeiro disparo, em micro-segundos
+
# Criar N threads, uma para somar os valores de cada linha.
  timer.it_value.tv_sec  = 3 ;      // primeiro disparo, em segundos
+
# Receber o resultado do somatório de cada linha e gerar o somatório total da matriz.
  timer.it_interval.tv_usec = 0 ;  // disparos subsequentes, em micro-segundos
+
# Analise o programa: há problemas de sincronização que precisam ser resolvidos? Se sim, resolva-os.
  timer.it_interval.tv_sec  = 1 ;  // disparos subsequentes, em segundos
 
 
 
  // arma o temporizador ITIMER_REAL (vide man setitimer)
 
  if (setitimer (ITIMER_REAL, &timer, 0) < 0)
 
  {
 
      perror ("Erro em setitimer: ") ;
 
      exit (1) ;
 
  }
 
 
 
  // laco vazio
 
  while (1) ;
 
}
 
</syntaxhighlight>
 
 
 
==== Implementação do trabalho ====
 
 
 
O mecanismo a ser implementado no BOOOS deve seguir as seguintes regras:
 
*Criaremos uma classe Timer, que configurará um '''interval timer''' de modo semelhante ao exemplo acima, e contabilizará o tempo do sistema através de um tratador de sinal do UNIX. O tempo deverá ser contabilizado em '''ticks'''. Este temporizador deverá ser configurado para operar com uma resolução de 1 ms. ''Dica:'' defina um parâmetro de configuração TIMER_RESOLUTION em BOOOS.h para tornar a resolução do timer configurável;
 
*Ao ganhar o processador, cada tarefa recebe um quantum de 20 ticks de relógio;
 
*Ao ser acionada, a rotina de tratamento de ticks deve verificar o período de notificação do escalonador e notificá-lo quando for a hora;
 
*Quando um quantum é completado, o controle da tarefa deve retornar ao dispatcher.
 
 
 
Abaixo o diagrama UML atualizado do sistema e uma versão do arquivo Timer.h COMPLETO. Baixem o novo gabarito do projeto [https://www.dropbox.com/s/npj64iphyqfi64m/booos.zip aqui]. Notem os presentes de páscoa em locais do código. :-)
 
Alguns detalhes sobre esta versão do projeto:
 
 
 
*'''Arquivos de configuração do sistema lib/BOOOS.h e lib/BOOOS.c''': Há uma classe com várias constantes declaradas. Estas constantes definem a configuração do sistema e podem ser utilizadas em qualquer local do programa (basta incluir lib/BOOOS.h);
 
*'''Scheduler::notify_time''': o Scheduler tem um método ''notify_time'' que é chamado pelo Timer sempre que o período de observação é atingido (ou seja, a cada 20 ticks);
 
*'''BOOOS::init()''': o método ''init()'' do sistema em lib/BOOOS.cc está correto como já implementado. Ele verifica a configuração em lib/BOOOS.h e inicializa o sistema de acordo;
 
*'''BOOOS_Configuration::SCHEDULER_TYPE''': esta variável seleciona a política de escalonamento. ''Dica: use ela para decidir como inserir elementos na fila de prontos.''.
 
*'''Timer::init(Scheduler * sched, Timestamp period)''': nosso Timer notifica apenas um observador: o Scheduler. Na inicialização, o Timer precisa tomar conhecimento do objeto Scheduler e do período de notificação;
 
*'''delay_ticks(Timestamp ticks)''': método para gerar um atraso baseado em ticks. Para implementá-lo, salve o '''__ticks''' atual e aguarde até que o '''__ticks''' do sistema chegue ao valor desejado;
 
*'''delay(Timestamp microseconds)''': método para gerar um atraso baseado em tempo (em microsegundos). Implemente-o de modo semelhante ao ''delay_ticks'', porém levando em conta a duração de um tick (''SCHEDULER_RESOLUTION'').
 
 
 
[[Arquivo:Booos-preempt.png|600px]]
 
  
 
<syntaxhighlight lang=cpp>
 
<syntaxhighlight lang=cpp>
/*
+
#include <iostream>
* Timer.h
+
#include "thread.h"
*
 
*  Created on: Mar 30, 2014
 
*      Author: arliones
 
*/
 
  
#ifndef TIMER_H_
+
/* number of matrix columns and rows */
#define TIMER_H_
+
#define M 5
 +
#define N 10
  
#include <signal.h>
+
using namespace std;
#include <unistd.h>
 
#include <sys/time.h>
 
#include <Queue.h>
 
  
namespace BOOOS {
+
int matrix[N][M];
 +
Thread *threads[N];
  
class Scheduler;
 
  
class Timer {
+
/* thread function; it sums the values of the matrix in the row */
 +
int SumValues(int i)
 +
{
 +
int n = i; /* number of row */
 +
int total = 0; /* the total of the values in the row */
 +
int j;
 +
for (j = 0; j < M; j++) /* sum values in the "n" row */
 +
total += matrix[n][j];
 +
cout << "The total in row" << n << " is " << total << "." << endl;
 +
/* terminate a thread and return a total in the row */
 +
exit(total);
 +
}
  
protected:
+
int main(int argc, char *argv[])
Timer() {}
+
{
 +
int i, j;
 +
int total = 0; /* the total of the values in the matrix */
  
public:
+
/* initialize the matrix */
 +
for (i = 0; i < N; i++)
 +
for (j = 0; j < M; j++)
 +
matrix[i][j] = i * M + j;
  
typedef unsigned long long Timestamp;
+
/* create threads */
 +
/* COLOQUE SEU CÓDIGO PARA CRIAR AS THREADS AQUI! */
  
virtual ~Timer() {}
+
/* wait for terminate a threads */
 +
/* COLOQUE SEU CÓDIGO PARA PEGAR O SOMATÓRIO DE LINHAS E TOTALIZAR A SOMA DA MATRIZ AQUI! */
  
static void init(Scheduler * sched, Timestamp period);
+
cout << "The total values in the matrix is " << total << endl;
static void start();
 
  
static Timestamp ticks();
+
return 0;
static Timestamp time();
+
}
 
 
static void delay_ticks(Timestamp ticks);
 
static void delay(Timestamp microseconds);
 
 
 
private:
 
static void sig_handler(int signum);
 
 
 
static Timestamp __ticks;
 
 
 
static itimerval __timer;
 
static struct sigaction __action;
 
 
 
static Scheduler * __scheduler;
 
static Timestamp __period;
 
};
 
 
 
} /* namespace BOOOS */
 
 
 
#endif /* TIMER_H_ */
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== Sobre novo mecanismo de testes ====
 
 
Os testes que passei a vocês são, agora, um programa de testes automatizados. Vocês não precisam modificá-los. Ao executar o teste, vocẽs verão uma saída como esta:
 
 
<syntaxhighlight lang=bash>
 
arliones.hoeller@sj-labdes-29463:~/Dropbox/ifsc/teaching/SOP29005/my_booos/test$ ./Scheduler_Test
 
Welcome to BOOOS - Basic Object Oriented Operating System!
 
This program will test the class: Scheduler
 
Starting tests for unit: Scheduler
 
Init: OK!
 
Creation and Destruction: OK!
 
FCFS: OK!
 
Priority with Aging: OK!
 
Priority without Aging: OK!
 
</syntaxhighlight>
 
 
Para não realizar o teste automático, basta editar a função main do teste e chamar diretamente a função de teste que deseja executar (exemplo: ''Priority_Scheduler_Test_Functions::test_scheduling_without_aging()'').
 
 
==== Observações ====
 
 
É importante evitar preempções dentro do dispatcher ou de funções da biblioteca, pois estas podem ter resultados imprevisíveis, como condições de disputa e instabilidade. Pode-se controlar a ocorrência de preempções de várias formas. Uma forma conveniente de implementar esse controle usa o conceito de tarefa de sistema:
 
 
*Tarefas críticas como o dispatcher são consideradas tarefas de sistema, pois sua execução correta é fundamental para o bom funcionamento do sistema; sua preempção deve ser evitada.
 
*As demais tarefas (Main, Pang, …Pung) são consideradas tarefas de usuário, pois executam código definido pelo usuário da biblioteca, e podem ser preemptadas quando necessário.
 
*O tratador de temporização no escalonador deve sempre verificar se a tarefa corrente é de usuário ou de sistema antes de preemptá-la devido ao fim de um quantum. Você pode usar o tipo SCHEDULER para testar isto.
 
 
==== Resumo do trabalho ====
 
*'''O que é?''' Classe implementando temporizador (Timer) integrado ao escalonador.
 
*'''Duração estimada:''' 4 horas.
 
*'''Dependências:''' Scheduler.
 
*'''Entrega:''' Até 11/04, por email.
 
 
 
=== t6: Contabilização de tarefas ===
 
 
Você irá adicionar mecanismos para contabilizar o uso do processador pelas tarefas em execução. Seu sistema deve produzir uma mensagem de saída com o seguinte formato, para cada tarefa que finaliza (incluindo o próprio dispatcher):
 
 
:Task 17 exit: response time 4955 ms, CPU time 925 ms, 171 activations
 
 
O tempo de resposta é o tempo decorrido desde que a tarefa foi criada (pelo seu construtor), até o momento em que encerra (na chamada a '''exit()'''). A Task pode utilizar a função '''Timer::time()''' para saber o instante de tempo em que o sistema  se encontra em cada tempo. O tempo de CPU é o tempo em que a tarefa permanece utilizando a CPU. O número de ativações é o número de vezes que o escalonador colocou a tarefa para executar.
 
 
==== Resumo do trabalho ====
 
*'''O que é?''' Mecanismo de contabilização de tempo de execução das tarefas.
 
*'''Duração estimada:''' 4 horas.
 
*'''Dependências:''' Scheduler, Task, Timer.
 
*'''Entrega:''' Até 11/04, por email.
 
 
== 04/04: Prova 1: Processos ==
 
 
*'''Prova será realizada na sala 09.'''
 
 
*Conteúdo da prova. ''Há listas de exercícios ao final do capítulo de alguns livros, especialmente do '''Fundamentos de sistemas operacionais'''.''
 
** Abraham Silberschatz, Peter Baer Galvin, Greg Gagne '''Fundamentos de sistemas operacionais'''; 8ª ed. Rio de Janeiro:LTC, 2010. 536p.  ISBN  9788521617471
 
***Parte I - Uma visão geral
 
***Parte II - Gerenciamento de processos
 
** Andrew S. Tanenbaum '''Sistemas operacionais modernos'''; 3ª ed. São Paulo:Pearson Education do Brasil, 2010. 672p.  ISBN  9788576052371
 
***Capítulo 1: Introdução
 
***Capítulo 2: Processos e threads
 
** Rômulo S. Oliveira; Alexandre S. Carissimi; Simão S. Toscani '''Sistemas Operacionais'''; 4ª ed. Porto Alegre:Bookman, 2010. 375p.  ISBN  9788577805211
 
***Capítulo 1: Introdução
 
***Capítulo 2: Multiprogramação
 
***Capítulo 3: Programação concorrente
 
 
 
== 07/04: Estruturas de memória ==
 
 
* Revisão da prova.
 
 
* [[http://wiki.sj.ifsc.edu.br/images/4/42/SOP29005-parte4.pdf slides]]
 
  
 +
=== Exercício 3 ===
  
== 11/04: Acompanhamento de projetos ==
+
Corrija os problemas de sincronização do programa abaixo.
 
 
== 14/04: Apresentação dos projetos p0 a p6 ==
 
 
 
<!--
 
Funciona/Apresentação:
 
 
 
Tiago/Ernani - A
 
Marcus/Leonan - A
 
Elton/Flavia - A
 
Danilo/Jean - B
 
Leticia/Tamara - A
 
Gustavo/ThiagoB
 
 
 
-->
 
 
 
== 25/04: Revisão para recuperação da prova 1 ==
 
 
 
* Revisão de escalonamento e sincronização de processos
 
* Dúvidas dos alunos
 
 
 
== 28/04: Recuperação da Prova 1 ==
 
* Prova na sala 09
 
 
 
== 05/05: Aula cancelada pelo professor ==
 
 
 
== 09/05: Estruturas de memória (cont.) ==
 
* [[http://wiki.sj.ifsc.edu.br/images/4/42/SOP29005-parte4.pdf slides]]
 
 
 
===t7: Tarefa main===
 
 
 
Desde o início temos forjado uma tarefa main na inicialização de Task. Contudo, não demos ainda toda a atenção necessária a esta tarefa. Neste trabalho, caso ainda não tenha sido feito, vocês devem extender o sistema de vocês para atender aos seguintes requisitos, caso ainda não tenha sido feito:
 
*O ponto de partida deve ser o sistema implementado para o t6;
 
*O programa principal (main) deverá ser tratado como uma tarefa, sendo escalonável da mesma forma que as demais tarefas instanciadas;
 
*Todas as tarefas poderão ser escalonadas a partir de sua criação, inclusive o main;
 
*Seu sistema deverá funcionar corretamente com o código abaixo (atenção ao exit ao final da main):
 
  
 
<syntaxhighlight lang=cpp>
 
<syntaxhighlight lang=cpp>
 
#include <iostream>
 
#include <iostream>
#include <queue>
+
#include <fstream>
#include <sstream>
+
#include <uuid/uuid.h>
#include <BOOOS.h>
+
#include <unistd.h>
#include <Scheduler.h>
+
#include <errno.h>
  
#define ASSERT(x,y) if(!(x)) return y;
+
#include "thread.h"
  
 
using namespace std;
 
using namespace std;
using namespace BOOOS;
 
  
Task *pang, *peng, *ping, *pong, *pung;
+
const int PRODS = 100;
 +
const int REP = 10;
  
void function(void * arg) {
+
const int CONS = 20;
int i;
 
  
Task::self()->nice(2*Task::self()->tid());
+
const int BUF_SIZE = 35;
  
for(i=0; i<10; i++) {
+
Thread * prods[PRODS];
cout << (char*)arg << " " << i << endl;
+
Thread * cons[CONS];
Timer::delay_ticks(25);
 
}
 
cout << (char*)arg << " End" << endl;
 
Task::self()->exit(0);
 
}
 
  
int main() {
+
uuid_t buffer[BUF_SIZE];
 +
static int prod_pos = 0;
 +
static int cons_pos = 0;
  
BOOOS_Configuration::SCHEDULER_TYPE = Scheduler::SCHED_PRIORITY;
+
bool finished = false;
BOOOS_Configuration::SCHEDULER_PREEMPT = true;
 
BOOOS_Configuration::SCHEDULER_AGING = true;
 
BOOOS::init();
 
  
cout << "Main Start" << endl;
+
int producer(int n)
 +
{
 +
cout << "Producer was born!" << endl;
  
pang = new Task(function, 1, (char*)"\tPang");
+
int rep = REP;
peng = new Task(function, 1, (char*)"\t\tPeng");
 
ping = new Task(function, 1, (char*)"\t\t\tPing");
 
pong = new Task(function, 1, (char*)"\t\t\t\tPong");
 
pung = new Task(function, 1, (char*)"\t\t\t\t\tPung");
 
  
while(Task::count() > 2) {
+
char fname[36+1];
Task::self()->nice(20);
 
Task::self()->yield();
 
}
 
  
cout << "Main End" << endl;
+
while(rep--)
 +
{
 +
if(++prod_pos == BUF_SIZE) prod_pos = 0;
 +
uuid_generate(buffer[prod_pos]);
 +
uuid_unparse(buffer[prod_pos], fname);
  
Task::self()->exit(0);
+
string name(fname,sizeof(uuid_t)*2 + 4);
 +
ofstream file(name.c_str());
 +
file << name;
 +
file.close();
 +
}
  
return 0;
+
exit(REP);
 
}
 
}
</syntaxhighlight>
 
  
==== Resumo do trabalho ====
+
int consumer(int n)
*'''O que é?''' Garantia de operação adequada da tarefa main.
+
{
*'''Duração estimada:''' 2 horas.
+
cout << "Consumer was born!" << endl;
*'''Dependências:''' t0 a t6.
 
*'''Entrega:''' Até 23/05, por email.
 
  
 +
char fname[36+1];
 +
int consumed = 0;
  
===t8: Operador Join===
 
  
O objetivo deste projeto é construir um método de sincronização denominado ''join'' que permite que uma tarefa espere a conclusão de outra tarefa, de forma similar à chamada POSIX ''wait'' para processos. Para isso, um método com a seguinte assinatura deve ser adicionado à classe ''Task'':
+
while(true)
 +
{
 +
if(finished) exit(consumed);
  
<syntaxhighlight lang=cpp>
+
consumed++;
int join(Task * t);
 
</syntaxhighlight>
 
  
Ao chamar ''Task::self()->join(b)'', onde b é um ponteiro para outra ''Task'', a tarefa atual (''Task::self()'') deve ser suspensa até a conclusão da tarefa ''b''. Quando a tarefa ''b'' terminar, isto é, chamar o método ''exit()'', a tarefa suspensa deve ser colocada de volta à fila de tarefas prontas. Caso a tarefa ''b'' não exista, ou já tenha encerrado, esta chamada deve retornar imediatamente, sem suspender a tarefa atual. Lembre-se que mais de uma tarefa pode aguardar pela mesma tarefa concluir, e que todas devem ser "acordadas" quando isto acontecer.
+
if(++cons_pos == BUF_SIZE) cons_pos = 0;
 +
uuid_unparse(buffer[cons_pos], fname);
  
O valor de retorno do método ''join'' é o código de encerramento da tarefa ''b'', ou seja, o valor passado como parâmetro à chamada do método ''exit()'', ou, -1 caso a tarefa indicada não exista.
+
{
 +
ifstream file(fname);
 +
if(!file.good()) continue;
 +
string str;
 +
file >> str;
 +
cout << "Consumed: " << str << endl;
 +
}
  
Seu sistema deve funcionar com o programa exemplo a seguir.
+
if(remove(fname)) cerr << "Error: " << errno << endl;
 +
}
  
<syntaxhighlight lang=cpp>
+
exit(consumed);
#include <iostream>
 
#include <queue>
 
#include <sstream>
 
#include <BOOOS.h>
 
#include <Scheduler.h>
 
 
#define ASSERT(x,y) if(!(x)) return y;
 
 
using namespace std;
 
using namespace BOOOS;
 
 
Task *pang, *peng, *ping, *pong, *pung;
 
 
void function(void * arg) {
 
int i;
 
 
Task::self()->nice(2*Task::self()->tid());
 
 
for(i=0; i<10; i++) {
 
cout << (char*)arg << " " << i << endl;
 
Timer::delay_ticks(25);
 
}
 
cout << (char*)arg << " End" << endl;
 
Task::self()->exit(2*Task::self()->tid());
 
 
}
 
}
 
int main() {
 
 
BOOOS_Configuration::SCHEDULER_TYPE = Scheduler::SCHED_PRIORITY;
 
BOOOS_Configuration::SCHEDULER_PREEMPT = true;
 
BOOOS_Configuration::SCHEDULER_AGING = true;
 
BOOOS::init();
 
 
cout << "Main Start" << endl;
 
 
pang = new Task(function, 1, (char*)"\tPang");
 
peng = new Task(function, 1, (char*)"\t\tPeng");
 
ping = new Task(function, 1, (char*)"\t\t\tPing");
 
pong = new Task(function, 1, (char*)"\t\t\t\tPong");
 
pung = new Task(function, 1, (char*)"\t\t\t\t\tPung");
 
 
while(Task::count() > 2) {
 
Task::self()->nice(20);
 
Task::self()->yield();
 
}
 
 
Task * Main = Task::self();
 
int result = 0;
 
  
cout << "Main wait pang... ";
+
int main()
result = Main->join(pang);
+
{
cout << "Result: " << result << endl;
+
    cout << "Massive Producer x Consumer Problem\n";
  
cout << "Main wait peng... ";
+
    // Create
result = Main->join(peng);
+
    for(int i = 0; i < PRODS; i++)
cout << "Result: " << result << endl;
+
    prods[i] = new Thread(&producer, i);
 +
    for(int i = 0; i < CONS; i++)
 +
    cons[i] = new Thread(&consumer, i);
  
cout << "Main wait ping... ";
+
    // Join
result = Main->join(ping);
+
    int status = 0;
cout << "Result: " << result << endl;
+
    int produced = 0;
 +
    int consumed = 0;
 +
    for(int i = 0; i < PRODS; i++)
 +
    {
 +
    prods[i]->join(&status);
 +
    produced += status;
 +
    }
  
cout << "Main wait pong... ";
+
    finished = true;
result = Main->join(pong);
 
cout << "Result: " << result << endl;
 
  
cout << "Main wait pung... ";
+
    for(int i = 0; i < CONS; i++)
result = Main->join(pung);
+
    {
cout << "Result: " << result << endl;
+
    cons[i]->join(&status);
 +
    consumed += status;
 +
    }
  
 +
    cout << "Total produced: " << produced << endl;
 +
    cout << "Total consumed: " << consumed << endl;
  
cout << "Main End" << endl;
+
    return 0;
Main->exit(0);
+
}
  
return 0;
 
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== Resumo do trabalho ====
+
Utilize o script abaixo para executar o programa. O script cria um diretório em /tmp/test onde são salvos os arquivos criados pelos produtores. Para ter certeza de que todos os arquivos foram adequadamente consumidos (i.e., deletados), o diretório /tmp/test deve estar vazio após a execução do programa.
*'''O que é?''' Mecanismo de sincronização de tarefas.
 
*'''Duração estimada:''' 4 horas.
 
*'''Dependências:''' t0 a t6.
 
*'''Entrega:''' Até 23/05, por email.
 
  
 +
<syntaxhighlight lang=bash>
 +
#!/bin/bash
  
===t9: Chamada Sleep===
+
export myrun_DIR=`pwd`
 
+
export myrun_TEST=/tmp/test
A chamada de sistema ''sleep(t)'' é uma chamada de sistema UNIX que suspende o '''processo''' por t microsegundos. Como nossas tarefas são implementadas dentro de um processo, em um modelo N:1, todas as threads seriam suspensas juntas.
 
  
O objetivo deste projeto é criar um método ''sleep'' na classe ''Task'' com a assinatura abaixo:
+
rm -rf $myrun_TEST
<syntaxhighlight lang=cpp>
+
mkdir $myrun_TEST
void sleep(int t);
+
cd $myrun_TEST
</syntaxhighlight>
+
$myrun_DIR/multi_prod_cons
Este método faz com que somente a tarefa corrente seja suspensa durante o período desejado, sem perturbar a execução das demais tarefas.
+
cd $myrun_DIR
 
 
Para implementar esta funcionalidade é necessário:
 
*Criar uma fila de "tarefas dormindo", separada da fila de prontas;
 
*Escrever o método ''sleep'', que coloca a tarefa solicitante na fila de tarefas adormecidas, calcula o instante em que a tarefa deverá ser acordad e devolve o controle ao ''dispatcher'';
 
*Periodicamente, o ''dispatcher'' deve percorrer a fila de tarefas adormecidas e devolver à fila de prontas aquelas que já podem "acordar".
 
 
 
Seu sistema deve funcionar com o código abaixo. Modifique os "couts" para imprimir também o tempo atual (''Timer::time()'') a fim de verificar se os tempos de "sono" estão corretos.
 
 
 
<syntaxhighlight lang=cpp>
 
#include <iostream>
 
#include <queue>
 
#include <sstream>
 
#include <BOOOS.h>
 
#include <Scheduler.h>
 
 
#define ASSERT(x,y) if(!(x)) return y;
 
 
using namespace std;
 
using namespace BOOOS;
 
 
Task *pang, *peng, *ping, *pong, *pung;
 
 
void function(void * arg) {
 
int i;
 
 
Task::self()->nice(2*Task::self()->tid());
 
 
for(i=0; i<10; i++) {
 
cout << (char*)arg << " " << i << endl;
 
Task::self()->sleep(25);
 
}
 
cout << (char*)arg << " End" << endl;
 
Task::self()->exit(2*Task::self()->tid());
 
}
 
 
int main() {
 
 
BOOOS_Configuration::SCHEDULER_TYPE = Scheduler::SCHED_PRIORITY;
 
BOOOS_Configuration::SCHEDULER_PREEMPT = true;
 
BOOOS_Configuration::SCHEDULER_AGING = true;
 
BOOOS::init();
 
 
cout << "Main Start" << endl;
 
 
pang = new Task(function, 1, (char*)"\tPang");
 
peng = new Task(function, 1, (char*)"\t\tPeng");
 
ping = new Task(function, 1, (char*)"\t\t\tPing");
 
pong = new Task(function, 1, (char*)"\t\t\t\tPong");
 
pung = new Task(function, 1, (char*)"\t\t\t\t\tPung");
 
 
while(Task::count() > 2) {
 
Task::self()->nice(20);
 
Task::self()->yield();
 
}
 
 
Task * Main = Task::self();
 
int result = 0;
 
 
cout << "Main wait pang... ";
 
result = Main->join(pang);
 
cout << "Result: " << result << endl;
 
 
cout << "Main wait peng... ";
 
result = Main->join(peng);
 
cout << "Result: " << result << endl;
 
 
cout << "Main wait ping... ";
 
result = Main->join(ping);
 
cout << "Result: " << result << endl;
 
 
cout << "Main wait pong... ";
 
result = Main->join(pong);
 
cout << "Result: " << result << endl;
 
 
cout << "Main wait pung... ";
 
result = Main->join(pung);
 
cout << "Result: " << result << endl;
 
 
 
cout << "Main End" << endl;
 
Main->exit(0);
 
 
return 0;
 
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== Resumo do trabalho ====
+
Problemas de sincronização a serem considerados:
*'''O que é?''' Mecanismo de delay com suspensão de tarefa.
+
#Limite de tamanho do buffer: os produtores só podem produzir se houver espaço no buffer - nunca pode haver mais que BUF_SIZE arquivos na pasta de troca de arquivos.
*'''Duração estimada:''' 6 horas.
+
#Uso da posição do buffer: verificar se a posição consumida já foi de fato produzida.
*'''Dependências:''' t0 a t6.
+
#Consumo: verificar se todos os arquivos produzidos foram consumidos.
*'''Entrega:''' Até 23/05, por email.
+
#Sistema de arquivos: verificar se os arquivos estão sendo sincronizados adequadamente no sistema de arquivos (ver ''man 2 sync'').
  
== 12/05: Estruturas de memória (cont.) ==
 
* [[http://wiki.sj.ifsc.edu.br/images/4/42/SOP29005-parte4.pdf slides]]
 
  
== 16/05: Acompanhamento de projetos ==
+
== 09/10: Laboratório: programação concorrente ==
  
== 19/05: Estruturas de memória (cont.) ==
+
== 15/10: Encaminhamento para palestra MCC ==
* [[http://wiki.sj.ifsc.edu.br/images/4/42/SOP29005-parte4.pdf slides]]
 
  
== 23/05: Acompanhamento de projetos ==
+
== 16/10: Revisão e correção de listas de exercícios ==
* [http://www.lisha.ufsc.br/teaching/os/exercise/hello.html A verdadeira hitória do "Hello World!"]
 
  
== 26/05: Acompanhamento de projetos ==
+
== 22/10: Prova 1 - Comunicação e coordenação de processos ==
  
 +
== 23/10: Gerenciamento de memória: Introdução ==
  
== 30/05: Estruturas de memória (cont.) ==
+
* [[Arquivo:SOP29005-parte5.pdf]]
* [[http://wiki.sj.ifsc.edu.br/images/4/42/SOP29005-parte4.pdf slides]]
 
  
 +
== 29/10 e 30/10: Gerenciamento de memória ==
  
=== tA: Construção de Semáforos ===
+
* [[Arquivo:SOP29005-parte5.pdf]]
  
O objetivo deste projeto é implementar uma classe semáforo. A interface da classe a ser implementada está apresentada abaixo. Note que cada semáforo tem um inteiro para guardar seu valor, e uma fila para controlar as tarefas que estão aguardando sua liberação.
+
=== Atividade prática ===
  
<syntaxhighlight lang=cpp>
+
Escreva um programa de computador que, dada a configuração de um sistema de paginação e um endereço de entrada, forneça informações sobre o endereço dado no referido sistema. Mais detalhes abaixo:
/*
 
* Semaphore.h
 
*
 
*  Created on: Mar 16, 2014
 
*      Author: arliones
 
*/
 
 
 
#ifndef SEMAPHORE_H_
 
#define SEMAPHORE_H_
 
  
namespace BOOOS
+
* Entradas do programa:
{
+
** Largura do endereço em bits
 
+
** Tamanho das páginas em bytes
class Semaphore
+
** Arquivo com tabela de páginas
{
+
** Endereço a ser traduzido
public:
 
    Semaphore(int i = 1);
 
    virtual ~Semaphore();
 
 
    void p();
 
    void v();
 
 
private:
 
    volatile int sem;
 
    Queue _waiting;
 
};
 
  
} /* namespace BOOOS */
+
* Saídas do programa:
 +
** Número de frames no sistema
 +
** Número da página (endereço lógico)
 +
** Número do frame (endereço físico) (Hit ou Page Fault?)
 +
** Deslocamento
  
#endif /* SEMAPHORE_H_ */
+
A tabela de páginas estará em um arquivo em modo texto contendo um mapeamento por linha, como o abaixo. Observe que o arquivo contém os números de página ou frame, e não endereços.
 +
<syntaxhighlight lang=text>
 +
0-10
 +
1-9
 +
2-20
 +
3-37
 +
4-1
 +
5-4
 +
6-7
 +
7-6
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Os métodos da classe devem implementar as seguintes funções:
+
A classe abaixo pode ser utilizada para processar o arquivo de entrada. Ela utiliza um map da STL para fazer o mapeamento. Você precisará implementar o método get_frame desta classe.
*'''Semaphore(int i = 1)''': o construtor deve inicializar o semáforo. Perceba que se o construtor for chamado sem parâmetros, o semáforo será binário, ou seja, pode ser utilizado para implementar acesso ordenado a uma sessão crítica. O valor do semáforo deve ser inicializado com o parâmetro passado.
 
*'''~Semaphore()''': deve finalizar o semáforo, acordando todas as tarefas que aguardavam por ele.
 
*'''p()''': esta chamada deve decrementar o valor do semáforo. Caso o resultado do decremento seja negativo, a tarefa deve bloquear e retornar à execução apenas quando um v() for chamado. No caso de bloquear a tarefa, a execução deve retornar ao dispatcher.
 
*'''v()''': esta chamada de incrementar o valor do semáforo. Esta tarefa nunca é bloqueante e a tarefa que executa nunca perde o processador. Se houver alguma tarefa aguardando no semáforo, a primeira tarefa da fila deve ser acordada e retornar à fila de prontas.
 
 
 
Observações:
 
* Pode (deve!) ser necessário modificar ou criar funções extra na classe Task para resolver este problema.
 
* Os semáforos devem ser implementados totalmente por você, ou seja, você NÃO deve usar outras bibliotecas ou funções do SO para implementar seu semáforo.
 
* Para que um semáforo funcione, as operações de p() e v() devem ser executadas de forma atômica, isto é, as trocas de contexto e interrupções de timer não podem acontecer durante a execução das funções do semáforo. Para isto, modifique sua implementação de timer de modo que a classe semáforo possa impedir, temporariamente, que o timer interrompa a execução de um método de semáforo.
 
 
 
=== tB: Uso de Semáforos ===
 
 
 
Para avaliar o uso do semáfor implementado no tA, você implementará, usando seu BOOOS, a aplicação produtor/consumidor. Já implementamos esta aplicação [http://wiki.sj.ifsc.edu.br/index.php/SOP29005-2014-1#Produtor.2FConsumidor antes], mas utilizando uma versão de semáforo que o Linux nos oferece. Adapte aquela aplicação para usar o seu semáforo e o Task::sleep() ao invés dos delays por laço.
 
 
 
 
 
=== tC: Fila de Mensagens ===
 
 
 
Uma fila de mensagens é uma estrutura usada por tarefas para comunicação de mensagens de tamanho fixo. O acesso à fila é bloqueante, ou seja, uma tarefa que tenta colocar uma mensagem em uma fila cheia terá de esperar até que surjam vagas; uma tarefa que deseja receber mensagens de uma fila vazia terá de esperar até que uma mensagem seja enviada para a fila. Isto lembra algo do trabalho anterior?
 
 
 
Filas de mensagens são na verdade buffers limitados acessados por tarefas que se comportam como produtoras e consumidoras de mensagens. A fila de mensagens em si é uma região crítica que deve ser protegida de eventuais condições de disputa. Para isso, você deve utilizar os semáforos desenvolvidos no módulo anterior em sua implementação.
 
 
 
Implementaremos um componente para nosso sistema que será chamado de Message_Queue, e tem a seguinte interface:
 
  
 
<syntaxhighlight lang=cpp>
 
<syntaxhighlight lang=cpp>
 
/*
 
/*
  * MessageQueue.h
+
  * MapFile.h
 
  *
 
  *
  *  Created on: Mar 17, 2014
+
  *  Created on: Oct 29, 2014
 
  *      Author: arliones
 
  *      Author: arliones
 
  */
 
  */
  
#ifndef MESSAGEQUEUE_H_
+
#ifndef MAPFILE_H_
#define MESSAGEQUEUE_H_
+
#define MAPFILE_H_
  
#include <BOOOS.h>
+
#include <string>
#include <Queue.h>
+
#include <map>
#include <string.h>
+
#include <fstream>
 +
#include <stdlib.h>
 +
#include <iostream>
  
namespace BOOOS
+
using namespace std;
{
 
  
class Message_Queue
+
class MapFile {
{
+
MapFile() {}
 
public:
 
public:
 +
MapFile(string filename)
 +
{
 +
ifstream file(filename.c_str());
 +
string line;
 +
char line_c[256];
 +
unsigned int page, frame, delimiter;
 +
while(!file.eof()) {
 +
file.getline(line_c,256); line = string(line_c);
 +
delimiter = line.find('-');
 +
page = atoi(line.substr(0,delimiter+1).c_str());
 +
frame = atoi(line.substr(delimiter+1).c_str());
 +
_pt.insert(make_pair(page,frame));
 +
}
  
    class Message: public Queue::Element
+
}
    {
 
    public:
 
        static const int MSG_SIZE = BOOOS_Configuration::MESSAGE_SIZE;
 
  
        Message(char msg[MSG_SIZE]) { memcpy(_msg, msg, MSG_SIZE); }
+
virtual ~MapFile() {}
        virtual ~Message() {}
 
  
        char * get() { return _msg; }
+
// Returns the number of the frame or -1 if not found
 +
unsigned int get_frame(unsigned int page)
 +
{
 +
//TODO
 +
}
  
    private:
+
void print_page_table()
        char _msg[MSG_SIZE];
+
{
    };
+
cout << "Page Table:" << endl;
 
+
map<unsigned int, unsigned int>::iterator mit = _pt.begin();
    Message_Queue(int max_size = 5);
+
for(; mit != _pt.end(); ++mit)
    virtual ~Message_Queue();
+
cout << mit->first << " - " << mit->second << endl;
 
+
}
    void send(Message & msg);
 
    Message receive();
 
 
 
    int count() { return _queue.length(); }
 
  
 
private:
 
private:
    Queue _queue;
+
map<unsigned int, unsigned int> _pt;
    int _max_size;
 
    // ... mais coisas?
 
 
};
 
};
  
} /* namespace BOOOS */
+
#endif /* MAPFILE_H_ */
 
 
#endif /* MESSAGEQUEUE_H_ */
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Os métodos da classe devem implementar as seguintes funções:
+
Abaixo, exemplos de execução do programa desejado:
*'''Message_Queue(ing max_size)''': cria uma fila de mensagens. Cada fila pode ter, no máximo, max_size mensagens.
+
<syntaxhighlight lang=bash>
*'''~Message_Queue()''': destroi uma fila de mensagens. Mensagens ainda na fila, mas não consumidas, devem ser destruídas. Tarefas bloqueadas aguardando por mensagens devem ser liberadas.
+
arliones@socrates:~/workspace/paging_sim$ ./paging_sim
*'''send(Message & msg)''': insere uma mensagem no final da fila. A mensagem deve ser criada externamente pela aplicação. Se a fila estiver cheia, este método bloqueia a tarefa até que espaço seja liberado na fila.
+
Usage: ./paging_sim ADDR_LEN PAGE_SIZE MAP_FILE ADDRESS
*'''Message receive()''': retorna uma cópia de uma mensagem recebida. Se a fila estiver vazia, esta chamada bloqueia a tarefa até que uma mensagem chegue.
 
*'''count()''': retorna a quantidade de mensagens na fila.
 
 
 
Sua Message_Queue pode ser testada com o programa abaixo. Repare que a aplicação não controla mais a concorrência. Ela delega esta responsabilidade à Message_Queue.
 
  
<syntaxhighlight lang=cpp>
+
arliones@socrates:~/workspace/paging_sim$ ./paging_sim 16 1024 page_table.txt 5687
/*
+
Frames in the system: 64
* Semaphore_Test.cc
+
Requested page: 5
*
+
Requested frame: 4
*  Created on: Jun 01, 2014
+
Offset: 567
*      Author: arliones
 
*/
 
  
#include <BOOOS.h>
+
arliones@socrates:~/workspace/paging_sim$ ./paging_sim 16 1024 page_table.txt 10578
#include <Task.h>
+
Frames in the system: 64
#include <MessageQueue.h>
+
Requested page: 10
 +
Requested frame: Page Fault
 +
Offset: 338
  
#include <iostream>
+
arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4096 page_table.txt 8632
 +
Frames in the system: 1048576
 +
Requested page: 2
 +
Requested frame: 20
 +
Offset: 440
  
using namespace std;
+
arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4096 page_table.txt 68723
using namespace BOOOS;
+
Frames in the system: 1048576
 +
Requested page: 16
 +
Requested frame: Page Fault
 +
Offset: 3187
  
Message_Queue queue;
+
arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4194304 page_table.txt 354
 +
Frames in the system: 1024
 +
Requested page: 0
 +
Requested frame: 10
 +
Offset: 354
  
static const int repetitions = 10;
+
arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4194304 page_table.txt 43554432
 +
Frames in the system: 1024
 +
Requested page: 10
 +
Requested frame: Page Fault
 +
Offset: 1611392
  
void producer(void *)
 
{
 
    cout << "Producer was born!" << endl;
 
 
    Task * myself = Task::self();
 
 
    char msg[26] = "This is message number 0.";
 
 
    for(int i = 0; i < repetitions; i++) {
 
        msg[23] = 0x30 + i;
 
        Message_Queue::Message qmsg(msg);
 
        queue.send(qmsg);
 
        myself->sleep(25000);
 
    }
 
 
    myself->exit(repetitions);
 
}
 
 
void consumer(void *)
 
{
 
    cout << "Consumer was born!" << endl;
 
 
    Task * myself = Task::self();
 
 
    for(int i = 0; i < repetitions; i++) {
 
        cout << queue.receive().get() << endl;
 
    }
 
 
    myself->exit(repetitions);
 
}
 
 
int main()
 
{
 
    cout << "Welcome to BOOOS - Basic Object Oriented Operating System!" << endl;
 
    cout << "This program will test the class: Semaphore" << endl;
 
 
    BOOOS_Configuration::SCHEDULER_TYPE = Scheduler::SCHED_FCFS;
 
    BOOOS_Configuration::SCHEDULER_PREEMPT = true;
 
    BOOOS_Configuration::SCHEDULER_AGING = true;
 
    BOOOS::init();
 
 
    Task * myself = Task::self();
 
 
    Task prod(&producer);
 
    Task cons(&consumer);
 
 
    myself->join(&prod);
 
    myself->join(&cons);
 
 
    myself->exit(0);
 
 
    return 0;
 
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== 02/06: Acompanhamento de projetos ==
+
Dicas: utilize variáveis ''unsigned'' do tipo adequado em seus cálculos. Você provavelmente precisará das funções ''log2'' e ''exp2'' da libmath (include math.h).
 
 
== 06/06: Gerenciamento de arquivos ==
 
 
 
* [[http://wiki.sj.ifsc.edu.br/images/c/c8/SOP29005-parte5.pdf slides]]
 
 
 
=== Estudo de caso: sistema de arquivos no Linux ===
 
  
Neste estudo de caso faremos alguns exercícios práticos que nos permitirão verificar como o sistema de arquivos é organizado no Linux.
+
== 12/11 e 13/11: Memória Virtual ==
  
Para isto, seguiremos [http://dainf.ct.utfpr.edu.br/~maziero/doku.php/unix:permissoes_em_arquivos este roteiro] do Prof. Maziero da UTFPR.
+
* [[Arquivo:SOP29005-parte5.pdf]]
  
== 09/06: Sistemas de arquivos ==
+
== 19/11 e 20/11: Sistemas de arquivos ==
  
* [[http://wiki.sj.ifsc.edu.br/images/c/c8/SOP29005-parte5.pdf slides]]
+
* [[Arquivo:SOP29005-parte6.pdf]]
  
* [http://www.cs.rutgers.edu/~pxk/416/notes/13-fs-studies.html Este material] do Prof. Krzyzanowski da Rutgers University descreve a implementação de alguns sistemas de arquivos.
 
  
== 13/06: Acompanhamento de projetos ==
+
== 26/11: Revisão para prova ==
  
== 16/06: Apresentação de projetos t7 a tC ==
+
* Dúvidas / Lista de exercícios.
 +
* Aula será no
  
* Notas atualizadas no início da página.
+
== 27/11: P2 (gerenciamento de memória) ==
  
== 23/06: Gerenciamento de entrada/saída ==
 
  
* [[http://wiki.sj.ifsc.edu.br/images/6/6f/SOP29005-parte6.pdf slides]]
+
== 03/12: Sistemas de arquivos: estudos de caso ==
  
== 27/06: Revisão para prova ==
+
* [[Arquivo:SOP29005-parte6.pdf]]
  
== 30/06: Revisão para prova ==
+
== 04/12: Gerenciamento de entrada e saída: introdução / Atividade em laboratório: construção de módulo para Linux ==
  
== 04/07: Prova 2 ==
+
* [[Arquivo:SOP29005-parte7.pdf]]
* Na sala 15
 
  
== 07/07: Encerramento, revisão para prova de recuperação ==
+
== 10/12: Revisão e correção de listas de exercícios ==
  
== 11/07: Prova de recuperação ==
+
== 11/12: P3 (sistemas de arquivo e gerenciamento de entrada e saída) ==

Edição das 23h45min de 2 de outubro de 2015

Publicada originalmente como SOP29005-2014-2

EngTel: Sistemas Operacionais - 2014-2

Plano de ensino

Cronograma de Atividades - Planejado

Cronograma de Atividades - Ajustado

Aula Data Horas Conteúdo Recursos
1 31/7 2 Apresentação da Disciplina. Visão geral de funções, responsabilidades e estruturas de um SO Lab. Informática
2 6/8 2 Atividades em laboratório: Introdução ao Linux e GCC Lab. Informática
3 7/8 2 Arquitetura de sistemas operacionais e modelos de programação Lab. Informática
4 13/8 2 Gerência de tarefas; contextos, processos e threads Lab. Informática
5 14/8 2 Atividades em laboratório: API POSIX – fork/wait – t0: biblioteca de filas Lab. Informática
6 20/8 2 Escalonamento de tarefas Lab. Informática
7 21/8 2 Atividades em laboratório: Estrutura de processos (a verdadeira história do Hello World) – t1: troca de contexto e tarefas cooperativas Lab. Informática
8 27/8 2 Atividade em laboratório: acompanhamento de projetos Lab. Informática
9 28/8 2 Algoritmos de escalonamento Lab. Informática
10 3/9 2 Algoritmos de escalonamento / Revisão para prova Lab. Informática
11 4/9 2 Revisão e correção de listas de exercícios Lab. Informática
12 10/9 2 P0 (introdução e gerência de tarefas) Lab. Informática
13 11/9 2 Atividades em laboratório: pthreads – t2: escalonamento FIFO e por prioridades Lab. Informática
14 17/9 2 Comunicação entre processos: Troca de mensagens e Memória Compartilhada Lab. Informática
15 18/9 2 Atividade em laboratório: programação com pipes e API shm – t3: Preempção e compartilhamento de tempo Lab. Informática
16 24/9 2 Coordenação entre processos Lab. Informática
17 25/9 2 Laboratório: Problemas clássicos de coordenação; impasses Lab. Informática
18 1/10 2 Laboratório: Problemas clássicos de coordenação; impasses Lab. Informática
19 2/10 2 Laboratório: Problemas clássicos de coordenação; impasses Lab. Informática
20 8/10 2 Laboratório: Problemas clássicos de coordenação; impasses Lab. Informática
21 9/10 2 Laboratório: Problemas clássicos de coordenação; impasses Lab. Informática
22 15/10 2 Encaminhamento para palestra MCC Lab. Informática
23 16/10 2 Revisão e correção de listas de exercícios Lab. Informática
24 22/10 2 P1 (comunicação e coordenação de tarefas) Lab. Informática
25 23/10 2 Gerenciamento de memória: Introdução Lab. Informática
26 29/10 4 Gerenciamento de memória: paginação e segmentação. Lab. Informática
27 30/10 2 Atividade em laboratório: alocação de memória. Lab. Informática
28 5/11 2 Aula adiantada em 29/out Lab. Informática
29 6/11 0 Aula adiada: viagem do professor Lab. Informática
30 12/11 2 Gerenciamento de memória: memória virtual Lab. Informática
31 13/11 2 Gerenciamento de memória: memória virtual Lab. Informática
32 19/11 2 Revisão e correção de listas de exercícios Lab. Informática
33 20/11 2 P2 (gerenciamento de memória) Lab. Informática
34 26/11 2 Sistemas de arquivos: introdução e controle de acesso Lab. Informática
35 27/11 2 Sistemas de arquivos: estudos de caso e gerenciamento de memória secundária Lab. Informática
36 3/12 2 Gerenciamento de entrada e saída: introdução / Atividade em laboratório: construção de módulo para Linux Lab. Informática
37 4/12 2 Revisão e correção de listas de exercícios Lab. Informática
38 10/12 2 P3 (sistemas de arquivo e gerenciamento de entrada e saída) Lab. Informática
TOTAL 76

Notas

Matrícula P0 P1 P2 P3 T0 T1 Final
122001838-4 A A B B A B A
122001432-0 A B B A A B A
122005026-1 A B A B A B A
122006899-3 B A C A A C B
122001913-5 B A A B A C B

Material de aula

Slides

Listas de exercícios

As listas de exercícios são compostas por exercícios selecionados do livro do Silberschatz, 8a edição. Há 10 volumes deste livro na biblioteca do campus, sendo suficiente para toda a turma deste semestre.

SILBERSCHATZ, Abraham; GALVIN, Peter; GAGNE, Greg. Fundamentos de sistemas operacionais. 8. ed. Rio de Janeiro: LTC, 2011. 515 p., il. ISBN 9788521617471.

Exercícios selecionados:

  • Parte 1
    • Capítulo 1: 1-3, 6-8, 10, 13, 14, 17, 22, 23, 25.
    • Capítulo 2: 1-8, 12, 13, 15, 17, 22, 25.
  • Parte 2
    • Capítulo 3: 1, 3, 6-10, 13, 15
    • Capítulo 4: 1, 4, 7, 8, 10-13
    • Capítulo 5: 1-3, 5, 6, 9, 10, 13-15, 21
  • Parte 3
    • Capítulo 6: 1, 2 (utilizar semáforos POSIX), 6, 8, 11-15, 18, 20, 21, 25, 29, 39.
  • Parte 4
    • Capítulo 8: 1-6, 9-21, 23.
    • Capítulo 9: 1-8, 14-16, 19-23, 28.
  • Parte 5
    • Capítulo 10: 1-20
    • Capítulo 11: 1-7
    • Capítulo 12: 1-7, 13-14 (desafio).

Projeto

Esta disciplina utiliza um projeto contínuo, no qual os alunos desenvolvem um aplicativo que emula um sistema operacional no espaço de usuário no Linux. Este projeto é chamado de BOOOS - Basic Object Oriented Operating System.

Diário de Aulas

31/07: Apresentação da Disciplina. Visão geral de funções, responsabilidades e estruturas de um SO

06/08: Laboratório: Linux e GCC/G++


Herança

Classes em C++ podem ser estendidas, criando novas classes que retêm as característica da classe-base. Este processo, conhecido como herança, envolve uma classe-base e uma classe derivada: a classe derivada herda os membros da classe-base, sobre os quais ela pode adicionar seus próprios membros.

Por exemplo, imaginemos uma série de classes para descrever dois tipos de polígonos: retângulos e triângulos. Estes dois polígonos têm certas propriedades em comum, como os valores necessários para calcular suas áreas: ambos podem ser descritos simplificadamente com uma altura e uma largura. (ou base).

Isto pode ser representado no formato de classes como uma classe Polygon (polígono) da qual podemos derivar duas outras classes: Rectangle e Triangle:

Inheritance.png

A classe Polygon poderia conter membros comuns a ambos os tipos de polígonos. Em nosso caso: largura e altura (width e height). E as classes Rectangle e Triangle poderiam ser as classes derivadas, com características específicas que diferenciam um tipo de polígono de outro.

Classes que são derivadas de outras herdam todos os membros acessíveis da classe-base. Isto significa que se a classe-base inclui um membro A e nós derivamos uma classe dela, e esta nova classe possui um membro B, a classe derivada conterá ambos os membros A e B.

No C++, a relação de herança de duas classes é declarada na classe derivada. A definição de classes derivadas usa a seguinte sintaxe:

class classe_derivada : public classe_base
{ /*...*/ };

O código acima define uma classe com nome classe_derivada, que herda publicamente a classe com nome classe_base. A palavra reservada public pode ser substituído por protected ou private, dependendo do tipo de herança desejado. Este delimitador de acesso limita o nível de acesso aos membros herdados da classe-base: os membros com um nível de acesso mais acessível são herdados com o nível declarado na herança, enquanto os membros com níveis de acesso iguais ou mais restritivos mantém, na classe derivada, os níveis de restrição declarados na classe-base.

#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:

    virtual int area() = 0;

    void set_values (int a, int b)
      { width=a; height=b;}
 };

class Rectangle: public Polygon {
  public:
    int area ()
      { return width * height; }
 };

class Triangle: public Polygon {
  public:
    int area ()
      { return width * height / 2; }
  };
  
int main () {
  Rectangle rect;
  Triangle trgl;
  rect.set_values (4,5);
  trgl.set_values (4,5);
  cout << rect.area() << '\n';
  cout << trgl.area() << '\n';
  return 0;
}
  • Experimento 1: compile e execute o exemplo acima.
  • Experimento 2: substitua, no exemplo acima, uma herança pública por herança protegida ou privada e verifique o que acontece.

Espaços de nomes

Espaços de nome, ou namespaces, permite agrupar entidades como classes, objetos e funções sob um mesmo nome. Deste modo o escopo global pode ser dividido em "sub-escopos", cada um com seu próprio nome.

A sintaxe para uso de um namespace em C++ é dada abaixo, onde identifier é o nome do sob o qual as entidades serão declaradas e, no local do comentário, seria registrado o conjunto de classes, objetos e funções incluídos no namespace:

namespace identifier
{
  /* entities... */
}

Por exemplo, o código abaixo as variáveis a e b são inteiros normais declarados dentro do namespace myNamespace.

namespace myNamespace
{
  int a, b;
}

Estas variáveis podem ser acessadas normalmente por classes ou funções declaradas dentro do mesmo namespace. Para serem acessadas de fora do namespace, contudo, elas precisam ser adequadamente qualificadas utilizando o operador de escopo (::). Por exemplo, para utilizar as variáveis acima de fora do myNamespace, elas devem ser qualificadas como:

myNamespace::a
myNamespace::b

Espaços de nomes podem ser bastante úteis para evitar colisão de identificadores:

// namespaces
#include <iostream>
using namespace std;

namespace foo
{
  int value() { return 5; }
}

namespace bar
{
  const double pi = 3.1416;
  double value() { return 2*pi; }
}

int main () {
  cout << foo::value() << '\n';
  cout << bar::value() << '\n';
  cout << bar::pi << '\n';
  return 0;
}
  • Experimento 1: compile, execute, e entenda o código do exemplo acima.
  • Experimento 2: crie, dentro do namespace bar, uma função que acesse a função value do namespace foo.

Criando bibliotecas

Uma biblioteca é uma coleção de objetos, assim como uma biblioteca tradicional é uma coleção de livros. Quando construindo seu programa, você pode utilizar, no gcc, uma ou mais bibliotecas, de modo que o gcc utilizará os objetos nestas bibliotecas para completar seu programa. Por exemplo, todas as funções da biblioteca padrão C (como printf e exit) estão em uma biblioteca C, geralmente na pasta lib/libc.a da sua instalação GCC. Quando você faz a ligação do seu programa, o GCC adiciona ao binário os objetos da biblioteca C necessários, baseando-se nas chamadas de funções do seu programa. Importante perceber que apenas as funções/objetos utilizados são ligados ao programa, não gerando desperdício de tempo e espaço.

Para fazer usa própria biblioteca, você precisa, primeiro, compilar cada um dos arquivos-fonte, gerando um conjunto de arquivos-objeto. Aqui utilizaremos, como exemplo, o código do exercício-exemplo da aula anterior.

g++ -c pessoa.cc
g++ -c aluno.cc

A seguir, você utilizará o comando ar para criar uma biblioteca contendo os arquivos-objeto criados.

ar rvs mylib.a pessoa.o aluno.o

Cada uma das letras em rvs especifica um parâmetro para o ar. r significa substituir objetos com mesmo nome na biblioteca pelos novos passados pela linha de comando. Como a biblioteca está inicialmente vazia, isto significa o mesmo que adicionar novos objetos à biblioteca. Há também opções para extrair e remover objetos da biblioteca. A opção v significa verbose, ou seja, pede que o programa ar imprima na tela as ações sendo tomadas durante sua execução. Finalmente, a opção s diz ao ar para criar uma tabela de símbolos, que é um recurso extra que o GCC precisa para utilizar uma biblioteca.

Para utilizar a biblioteca, simplesmente adicione ela ao comando de ligação do gcc como se fosse outro objeto:

g++ main.cc mylib.a -o main

É importante listar as bibliotecas na ordem correta. Durante a ligação, o GCC "puxa" apenas os objetos que sabe necessitar até o momento. Isto que dizer que primeiro é necessário alimentar ao GCC os arquivos-objeto que dependem de uma biblioteca (no exemplo, o main.cc), e por fim as bibliotecas que completam esta dependência.

  • Experimento 1: pegue o código-fonte da aula anterior e gere a biblioteca mylib.a utilizando os comandos acima.
  • Experimento 2: modifique o arquivo makefile da aula anterior para trabalhar com a biblioteca.
  • Experimento 3: modifique a assinatura de algum método das classes Pessoa ou Aluno e verifique o que acontece.

07/08: Arquitetura de sistemas operacionais e modelos de programação

  • Apresentação sobre histórico visão geral e estruturas básicas de um SO (Arquivo:SOP29005-parte1.pdf)
  • Capítulo 2 do livro do Silberschatz

13/08: Gerência de tarefas; contextos, processos e threads

14/08: Atividades em laboratório: API POSIX – fork/wait

Roteiro de exercícios: gerenciamento de processos

Syscall FORK

  • Em um terminal, execute "man fork"
    • A função da API POSIX fork() aciona uma chamada de sistema (system call - syscall) que cria um novo processo duplicando o processo que realiza a chamada. O novo processo, chamado de filho, é uma cópia exata do processo criador, chamado de pai, exceto por alguns detalhes listados na manpage. O principal destes detalhes para nós agora é o fato de terem PIDs diferentes.
    • O código dos dois processos (pai e filho) são idênticos;
    • Os dados dos dois processos (pai e filho) são idênticos NO MOMENTO DA CRIAÇÃO;
    • Execução do processo filho inicia na próxima instrução do programa (no retorno da chamada FORK);
    • Não é possível saber qual dos processos (pai ou filho) retormará a execução primeiro - isto fica a cargo do excalonador do SO;
    • Valores de retorno da chamada FORK:
      • (-1): erro na criação do processo (ex.: memória insuficiente);
      • (0): em caso de sucesso, este é o valor de retorno recebido pelo processo filho;
      • (>0): em caso de sucesso, este é o valor de retorno recebido pelo processo pai;

Syscall JOIN

  • A syscall JOIN é implementada no POSIX pelo função wait(). Execute "man wait".
    • Além da função wait(), há também waitpid() e waitid();
    • Todas estas syscalls são utilizadas para aguardar por mudanças no estado de um processo filho e obter informações sobre o processo filho cujo estado tenha mudado. São consideradas mudanças de estado: o filho terminou; o filho foi finalizado por um sinal (ex.: kill); o filho foi retomado por um sinal (ex.: alarme);
    • A chamada wait também libera os recursos do processo filho que termina;
    • wait(): esta função suspende a execução do processo chamador até que UM DOS SEUS FILHOS finalize;
    • waitpid(): suspende a execução do processo chamador até que UM FILHO ESPECÍFICO finalize;

Syscall EXEC

  • A syscall EXEC é implementada no POSIX pela família de funções exec(). Execute "man exec".
    • As principais funções da família são execl(), execlp() e execvp();
    • Todas estas funções são, na realidade, front-ends (abstrações) para a syscall execve. Esta syscall substitui a imagem do processo corrente (aquele que chama a syscall) pela a imagem de um novo processo;
    • Os parâmetros passados a estas funções são, basicamente, o nome de um arquivo com a imagem do programa a ser executado (um binário de um programa), e uma lista de parâmetros a serem passados a este novo programa;


Exemplos POSIX utilizando fork/wait/exec

  • Exemplo 1: fork/wait básico
// ex1: fork/wait básico
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
    int pid, status;
    pid = fork();

    if(pid == -1) // fork falhou
    {
        perror("fork falhou!");
        exit(-1);
    }
    else if(pid == 0) // Este é o processo filho
    {
        printf("processo filho\t pid: %d\t pid pai: %d\n", getpid(), getppid());
        execl("/bin/ls","ls", (char*) NULL);
        perror("execl falhou!");
        exit(0);
    }
    else // Este é o processo pai
    {
        wait(&status);
        printf("processo pai\t pid: %d\t pid pai: %d\n", getpid(), getppid());
        exit(0);
    }
}
arliones@socrates:~/tmp$ gcc ex1.c -o ex1 
arliones@socrates:~/tmp$ ./ex1 
processo filho	 pid: 27858	 pid pai: 27857
processo pai	 pid: 27857	 pid pai: 5337
arliones@socrates:~/tmp$
  • Exemplo 2: processos pai e filho compartilham código, mas não dados.
// ex2: fork/wait "compartilhando" dados
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
    int pid, status, k=0;
    printf("processo %d\t antes do fork\n", getpid());
    pid = fork();
    printf("processo %d\t depois do fork\n", getpid());

    if(pid == -1) // fork falhou
    {
        perror("fork falhou!");
        exit(-1);
    }
    else if(pid == 0) // Este é o processo filho
    {
        k += 1000;
        printf("processo filho\t pid: %d\t K: %d\n", getpid(), k);
        exit(0);
    }
    else // Este é o processo pai
    {
        wait(&status);
        k += 10;
        printf("processo pai\t pid: %d\t K: %d\n", getpid(), k);
        exit(0);
    }
    k += 10;
    printf("processo %d\t K: %d\n", getpid(), k);
    exit(0);
}
arliones@socrates:~/tmp$ gcc ex2.c -o ex2
arliones@socrates:~/tmp$ ./ex2 
processo 18425	 antes do fork
processo 18425	 depois do fork
processo 18426	 depois do fork
processo filho	 pid: 18426	 K: 1000
processo pai	 pid: 18425	 K: 10
arliones@socrates:~/tmp$
  • Modificação no código: comentar linhas 22 e 29
arliones@socrates:~/tmp$ gcc ex2.c -o ex2
arliones@socrates:~/tmp$ ./ex2 
processo 32342	 antes do fork
processo 32342	 depois do fork
processo 32343	 depois do fork
processo filho	 pid: 32343	 K: 1000
processo 32343	 K: 1010
processo pai	 pid: 32342	 K: 10
processo 32342	 K: 20
arliones@socrates:~/tmp$
  • Analise os resultados e busque entender a diferença.


Exercício fork/wait

Excrever um programa C que cria uma árvore de 3 processos, onde o processo A faz um fork() criando um processo B, o processo B, por sua vez, faz um fork() criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY são PIDs de processos. Utilizar wait() para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar sleep() (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.


20/08: Escalonamento de tarefas

21/08: Atividades em laboratório: Estrutura de processos


Troca de contexto em nível de usuário

O Linux, através da API POSIX, oferece um conjunto de funções que permite às aplicações manipular contextos, facilitando a vida do programador que quer implementar tarefas "simultâneas" dentro de um único processo, ou seja, threads. As seguintes funções e tipos estão disponíveis:

  • getcontext(&a): salva o contexto na variável a;
  • setcontext(&a): restaura um contexto salvo anteriormente na variável a;
  • swapcontext(&a,&b): salva o contexto atual na variável a e restaura o contexto salvo anteriormente na variável b;
  • makecontext(&a, ...): ajusta parâmetros internos do contexto salvo em a;
  • ucontext_t: as variáveis a e b são do tipo ucontext_t. Este tipo armazena um contexto.

Busque mais informações sobre estas funções utilizando o programa manpage do Linux (ex.: man getcontext).

Estude o código no arquivo pingpong.c abaixo e explique seu funcionamento.

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

#define STACKSIZE 32768		/* tamanho de pilha das threads */

/* VARIÁVEIS GLOBAIS */
ucontext_t cPing, cPong, cMain;

/* Funções-comportamento das Tarefas */
void f_ping(void * arg) {
   int i;

   printf("%s iniciada\n", (char *) arg);

   for (i=0; i<4; i++) {
      printf("%s %d\n", (char *) arg, i);
      swapcontext(&cPing, &cPong);
   }
   printf("%s FIM\n", (char *) arg);

   swapcontext(&cPing, &cMain);
}

void f_pong(void * arg) {
   int i;

   printf("%s iniciada\n", (char *) arg);

   for (i=0; i<4; i++) {
      printf("%s %d\n", (char *) arg, i);
      swapcontext(&cPong, &cPing);
   }
   printf("%s FIM\n", (char *) arg);

   swapcontext(&cPong, &cMain);
}

/* MAIN */
int main(int argc, char *argv[]) {
   char *stack;

   printf ("Main INICIO\n");

   getcontext(&cPing);
   stack = malloc(STACKSIZE);
   if(stack) {
      cPing.uc_stack.ss_sp = stack ;
      cPing.uc_stack.ss_size = STACKSIZE;
      cPing.uc_stack.ss_flags = 0;
      cPing.uc_link = 0;
   }
   else {
      perror("Erro na criação da pilha: ");
      exit(1);
   }

   makecontext(&cPing, (void*)(*f_ping), 1, "\tPing");

   getcontext(&cPong);
   stack = malloc(STACKSIZE);
   if(stack) {
      cPong.uc_stack.ss_sp = stack ;
      cPong.uc_stack.ss_size = STACKSIZE;
      cPong.uc_stack.ss_flags = 0;
      cPong.uc_link = 0;
   }
   else {
      perror("Erro na criação da pilha: ");
      exit(1);
   }

   makecontext (&cPong, (void*)(*f_pong), 1, "\tPong");

   swapcontext(&cMain, &cPing);
   swapcontext(&cMain, &cPong);

   printf("Main FIM\n");

   exit(0);
}

27/08: Atividades em laboratório: acompanhamento de projetos

28/08: Algoritmos de escalonamento


03/09: Algoritmos de escalonamento / Revisão para prova

04/09: Listas de exercícios / Revisão para prova

10/09: Prova 0 - Processos

11/09: Atividades em laboratório: pthreads – t2: escalonamento FIFO e por prioridades

  • Conclusão do conteúdo da aula anterior

Introdução a POSIX Threads

A biblioteca POSIX Threads, ou simplesmente pthreads, é parte do padrão POSIX para programar utilizando threads. O padrão POSIX.1c define a API para criação e manipulação de threads. Esta API é encontrada na maioria dos sistemas baseados no Unix, como Linux, Mac OS X, Solaris, entre outros. Também existem alternativas adaptando a API para sistemas Windows, como a pthreads-w32.

A API da pthreads inclui métodos para criar, manipular e destruir threads, além de outras estruturas de dados para sincronizar as threads, incluindo implementações de mutexes e variáveis de condição.

A API POSIX semaphore, utilizada para sincronização de processos ou threads, também funciona em conjunto com a pthreads, embora sua implementação esteja definida em outro padrão, o POSIX.1b.

Programas em C/C++ que utilizarão a pthreads devem incluir o cabeçalho pthread.h:

#include <pthread.h>


Principais elementos da API

  • pthread_t (struct)
Estrutura que armazena dados/atributos de uma pthread.
  • pthread_create
Função que cria uma thread, incializando-a e deixando-a pronta para executar. O código abaixo apresenta um exemplo simples de um programa utilizando pthreads.
#include <pthread.h>

pthread_t threads[2];

void *thread_func(void *arg) {
    // ...
}

int main(int argc, char **argv) {
    int i;
    for(i = 0; i < 2; i++) {
        pthread_create(&(threads[i]), NULL, thread_func,NULL);
    }
    for(i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }
}


  • pthread_join
Bloqueia execução de uma thread até que outra thread termine. Similar à chamada de sistema wait usada em processos Unix.
  • pthread_exit
Encerra a execução de uma thread. A chamada a esta função por uma thread gera a liberação de outras threads que estejam, eventualmente, bloqueadas nela por uma chamada pthread_join.

O código abaixo apresenta um programa completo utilizando pthreads, onde threads recebem argumentos.

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

typedef struct {
    int idx, length;
} thread_arg, *ptr_thread_arg;

pthread_t threads[2];

void *thread_func(void *arg) {
    ptr_thread_arg targ = (ptr_thread_arg) arg;
    int i;

    for(i = targ->idx; i < (targ->idx + targ->length); i++)
        printf(Thread %d  value %d\n, pthread_self(), i);
}

int main(int argc, char **argv) {
    thread_arg arguments[2];
    int i;
    for(i = 0; i < 2; i++) {
        arguments[i].idx = i * 10;
        arguments[i].length = 10;
        pthread_create(&(threads[i]), NULL, thread_func, &(arguments[i]));
    }
    for(i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }
}

Ao compilar um programa com pthreads é necessário "linkar" com a biblioteca. Para isso, deve ser usando a opção -lpthread com o gcc.

gcc ... -lpthread

Exercícios

  • Implemente o exemplo do Ping e Pong utilizando pthreads.


17/09 e 18/09: Comunicação entre processos

Comunicação entre processos: Troca de mensagens

Um mecanismo disponibilizado por sistemas UNIX para troca de mensagens entre processos é o PIPE. Pipes são mecanismos de comunicação indireta onde mensagens são trocadas através de mailboxes. Cada mailbox possui um identificador único, permitindo que processos identifiquem o canal de comunicação entre eles. O fluxo de mensagens em um Pipe é:

  • unidirecional: sobre um mesmo pipe, apenas um processo envia mensagens e um processo recebe mensagens;
  • FIFO: as mensagens são entregues na ordem de envio;
  • não-estruturado: não há estrutura pré-definida para o formato da mensagem.

No UNIX, pipes são inicializados através da SystemCall pipe, que possui a seguinte sintaxe:

  • int pipe(int pipefd[2]): pipe inicializa um novo pipe no sistema e retorna, no array pipefd, os descritores identificando cada uma das pontas do pipe. A primeira posição do array, i.e. pipefd[0], recebe o descritor que pode ser aberto apenas para leitura, enquanto a segunda posição do array, i.e. pipefd[1], recebe o descritor que pode ser aberto apenas para escrita. A função retorna zero no caso de sucesso, ou -1 se ocorrer erro.

As primitivas send/receive para uso de um pipe no UNIX são implementadas por SystemCalls read/write, conforme segue:

  • ssize_t read(int fd, void *buf, size_t count): “puxa” dados do pipe identificado pelo descritor fd. Os dados recebidos são os apontados pelo ponteiro buf, sendo count a quantidade máxima de bytes a serem recebidos. A função retorna o número de bytes recebidos.
  • ssize_t write(int fd, const void *buf, size_t count): “empurra” dados no pipe identificado pelo descritor fd. Os dados transmitidos são os apontados pelo ponteiro buf, sendo count a quantidade de bytes a serem transmitidos. A função retorna o número de bytes transmitidos.

Abaixo há um exemplo de programa criando um pipe e compartilhando os descritores entre dois processos (criados via fork()).

#include <unistd.h>   
#include <fcntl.h>
#include <stdio.h>

char *message = "This is a message!!!" ;

main()
{
    char buf[1024] ;
    int fd[2];
    pipe(fd);    /*create pipe*/
    if (fork() != 0) { /* I am the parent */
        write(fd[1], message, strlen (message) + 1) ;
    }
    else { /*Child code */
        read(fd[0], buf, 1024) ;
        printf("Got this from MaMa!!: %s\n", buf) ;
    }
}
  • Desafio 1: construa um “pipeline”. Crie um programa que conecta 4 processos através de 3 pipes. Utilize fork() para criar vários processos.
  • Desafio 2: cópia de arquivo. Projete um programa de cópia de arquivos chamado FileCopy usando pipes comuns. Esse programa receberá dois parâmetros: o primeiro é o nome do arquivo a ser copiado e o segundo é o nome do arquivo copiado. Em seguida, o programa criará um pipe comum e gravará nele o conteúdo do arquivo a ser copiado. O processo filho lerá esse arquivo do pipe e o gravará no arquivo de destino. Por exemplo, se chamarmos o programa como descrito a seguir:
$ FileCopy entrada.txt copia.txt
o arquivo entrada.txt será gravado no pipe. O processo filho lerá o conteúdo desse arquivo e o gravará no arquivo de destino copia.txt. Escreva o programa usando os pipes da API POSIX no Linux.


Comunicação entre processos: Memória Compartilhada

Outra maneira de compartilhar dados entre processos é utilizando memória compartilhada. Nestes casos, o sistema operacional precisa ser utilizado para "mapear" blocos de memória no espaço de endereçamento de cada processo (veremos mais tarde como isto é feito). Sistemas UNIX diponibilizam uma série de SystemCalls para compartilhar memória entre processos:

  • int shmget(key_t key, size_t size, int shmflg): retorna o identificador de um segmento de memória compartilhado associado a uma chave de identificação (key). Caso a chave solicitada não exista e a flag IPC_CREAT está especificada, um novo segmento de memória compartilhada é criado, com o tamanho size.
  • void *shmat(int shmid, const void *shmaddr, int shmflg): anexa/mapeia o segmento de memória especificado por shmid (obtido através de um shmget) no endereço shmaddr do espaço de endereçamento do processo.
  • int shmdt(const void *shmaddr): desfaz o mapeamento do segmento de memória compartilhada que está mapeado em shmaddr.
  • int shmctl(int shmid, int cmd, struct shmid_ds *buf): realiza a ação de controle indicada por cmd sobre o segmento de memória compartilhada identificado pelo shmid. Caso o cmd utilizado necessite de parâmetros, estes são passados em buf.

O programa abaixo cria um novo segmento de memória compartilhada em um sistema UNIX. A função ftok é utilizada para gerar uma chave única de identificação do segmento de memória (veja "man ftok"). O programa ipcs pode ser utilizado para verificar os segmentos de memória compartilhada disponíveis no sistema.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define SHM_SIZE 1024  /* make it a 1K shared memory segment */
 
int main(void)
{
 
       key_t key;
       int shmid;
       char *data;
       int mode;
 
       /* make the key: */
       if ((key = ftok("/tmp", 'X')) == -1) {
            perror("ftok");
            exit(1);
       }
 
       /* create the shared memory segment: */
       if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT )) == -1) {
            perror("shmget");
            exit(1);
       }
 
       return(0);
}

O programa abaixo manipula o segmento criado pelo programa anterior. Perceba que o programa utiliza a função ftok com os mesmos parâmetros passados na criação. O sistema operacional utiliza esta chava única para identificar o segmento de memória a ser utilizado. Se o programa abaixo é executado com um string como parâmetro (ex.: "./shm_test abc123"), ele escreve este parâmetro no segmento de memória. Caso seja executado sem parâmetros (ex.: "./shm_test"), o programa imprime o conteúdo da memória compartilhada.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define SHM_SIZE 1024  /* make it a 1K shared memory segment */
 
int main(int argc, char *argv[])
{
 
       key_t key;
       int shmid;
       char *data;
       int mode;
 
 
       /* make the key: */
       if ((key = ftok("/tmp", 'X')) == -1) {
            perror("ftok");
            exit(1);
       }
 
 
       /* connect to the segment.
      NOTE: There's no IPC_CREATE. What happens if you place IPC_CREATE here? */
       if ((shmid = shmget(key, SHM_SIZE, 0644)) == -1) {
            perror("shmget");
            exit(1);
       }
 
 
    /* attach to the segment to get a pointer to it: */
        data = shmat(shmid, (void *)0, 0);
        if (data == (char *)(-1)) {
            perror("shmat");
            exit(1);
        }
 
 
        /* read or modify the segment, based on the command line: */
        if (argc == 2) {
            printf("writing to segment: \"%s\"\n", argv[1]);
            strncpy(data, argv[1], SHM_SIZE);
        } else
            printf("segment contains: \"%s\"\n", data);
 
 
        /* detach from the segment: */
        if (shmdt(data) == -1) {
            perror("shmdt");
            exit(1);
        }
 
       return(0);
}
  • Desafio1: destrua um shm. Crie um programa que destrua o shm utilizado nos programas anteriores. Para isso utilize shmctl com o parâmetro apropriado (veja "man shmctl").


24/09: Coordenação de processos


25/09: Laboratório: Impasses e Problemas clássicos de coordenação

POSIX pthread mutex

A biblioteca pthread implementa um tipo pthread_mutex_t, que garante a exclusão mútua entre threads. Estes mutex são manipulados através das funções (acesse as man-pages das chamadas para maiores detalhes):

  • pthread_mutex_lock: acessa um mutex.
  • pthread_mutex_trylock: tenta acessar um mutex (retorna valor indicando sucesso ou falha no lock).
  • pthread_mutex_unlock: libera um mutex.
#ifndef __mutex_h
#define __mutex_h

#include <pthread.h>

class Mutex
{
public:
    Mutex() {}
    ~Mutex() {}

    void lock() { pthread_mutex_lock(&mut); }
    bool try_lock() { return (pthread_mutex_trylock(&mut) == 0); } // true when succeeds.
    void unlock() { pthread_mutex_unlock(&mut); }

private:
    pthread_mutex_t mut;
};

#endif

POSIX Semaphores

Nos sistemas POSIX, semáforos são implementados pelo tipo sem_t e manipulado através das funções (acesse as man-pages das chamadas para maiores detalhes):

  • sem_init: inicializa um semáforo;
  • sem_destroy: destroy um semáforo;
  • sem_wait: implementa a operação p;
  • sem_post: implementa a operação v.

Para utilizar estas funções é necessário linkar o programa à librt ou à libpthread (-lrt ou -lpthread). A classe C++ abaixo abstrai estas operações:

#ifndef __semaphore_h
#define __semaphore_h

#include <semaphore.h>

class Semaphore
{
public:
    Semaphore(int i = 1) { sem_init(&sem, 0, i); }
    ~Semaphore() { sem_destroy(&sem); }

    void p() { sem_wait(&sem); }
    void v() { sem_post(&sem); }

    operator int()
    {
        int ret;
        sem_getvalue(&sem, &ret);
        return ret;
    }

private:
    sem_t sem;
};

#endif

Exemplo de uso do operator:

Semaphore sem;
cout << (int)sem << endl;

POSIX Threads

A API POSIX disponibiliza uma biblioteca de threads chamada pthread. As threads são implementadas pela estrutura pthread_t, e manipuladas pelas funções (acesse as man-pages das chamadas para maiores detalhes):

  • pthread_create: cria uma thread;
  • pthread_kill: força a terminação de uma thread;
  • pthread_join: sincroniza o final de uma thread (qual a diferença/semelhança com o wait que usamos para processos?);
  • pthread_exit: finaliza uma thread.

Para utilizar estas funções é necessário linkar o programa à libpthread (-lpthread). A classe C++ abaixo abstrai estas operações:

#ifndef __thread_h
#define __thread_h

#include <pthread.h>
#include <signal.h>

class Thread
{
public:
    Thread(int ( * const entry)(int), int arg) {
	if(pthread_create(&thread, 0, (void*(*)(void*))entry, (void *)arg))
	    thread = 0;
    }
    ~Thread() { pthread_kill(thread, SIGKILL); }

    int join(int * status) { return pthread_join(thread, (void **)status); }
    friend void exit(int status = 0) { pthread_exit((void *) status); }

private:
    pthread_t thread;
};

#endif

Problemas clássicos de sincronização

Produtor/Consumidor

O problema clássico Produtor/Consumidor consiste em dois fluxos de execução (threads/processos), sendo que um dos fluxos (consumidor) só pode executar a partir do momento em que seus dados de entrada foram produzidos pelo outro fluxo (produtor).

  • DESAFIO: O programa abaixo implementa um produtor/consumidor utilizando semáforos para sincronização. Contudo, as chamadas para as operações v e p foram removidas, conforme comentários no código. Corrija este programa.
#include <iostream>
#include "thread.h"
#include "semaphore.h"

using namespace std;

const int DELAY = 10000000;
const int REP = 5;

Semaphore empty(1);
Semaphore full(0);

int producer(int n)
{
    cout << "Producer was born!\n";

    // Faltam, no laço abaixo:
    //  - uma chamada para empty.p()
    //  - uma chamada para full.v()
    for(int i = 0; i < REP; i++) {
	cout << "Producing ...\n";
	for(int i = 0; i < DELAY * 10; i++);
	cout << "Storing ...\n";
	for(int i = 0; i < DELAY; i++);
    }

    return n;
}

int consumer(int n)
{
    cout << "Consumer was born!\n";

    // Faltam, no laço abaixo:
    //  - uma chamada para full.p()
    //  - uma chamada para empty.v()
    for(int i = 0; i < REP; i++) {
	cout << "Retrieving ...\n";
	for(int i = 0; i < DELAY * 10; i++);
	cout << "Consuming ...\n";
	for(int i = 0; i < DELAY; i++);
    }

    return n;
}

int main()
{
    cout << "The Producer x Consumer Problem\n";

    Thread prod(&producer, REP);
    Thread cons(&consumer, REP);

    int status;
    prod.join(&status);
    if(status == REP)
	cout << "Producer went to heaven!\n";
    else
	cout << "Producer went to hell!\n";

    cons.join(&status);
    if(status == REP)
	cout << "Consumer went to heaven!\n";
    else
	cout << "Consumer went to hell!\n";

    return 0;
}

Jantar dos Filósofos

O problema clássico Jantar dos Filósofos consiste em que n fluxos (n filósofos) disputam n recursos (n talheres). No problema, para conseguir "jantar" (ou executar), cada filósofo precisa pegar dois talheres adjascentes a ele. Cada recurso é compartilhado por dois filósofos.

  • DESAFIO: O programa abaixo implementa um Jantar dos Filósofos utilizando semáforos para sincronização. Contudo, as chamadas para as operações v e p foram removidas, conforme comentários no código. Corrija este programa.
#include <iostream>
#include "thread.h"
#include "semaphore.h"

using namespace std;

const int DELAY = 10000000;
const int ITERATIONS = 5;

Semaphore chopstick[5];

int philosopher(int n)
{
    cout << "Philosopher " << n << " was born!\n";

    int first = (n < 4)? n : 0; // left for phil 0 .. 3, right for phil 4
    int second = (n < 4)? n + 1 : 4; // right for phil 0 .. 3, left for phil 4

    // Foram removidos do laço abaixo:
    //  - uma chamada para chopstick[first].p()
    //  - uma chamada para chopstick[second].p()
    //  - uma chamada para chopstick[first].v()
    //  - uma chamada para chopstick[second].v()
    for(int i = 0; i < ITERATIONS; i++) {
	cout << "Philosopher " << n << " thinking ...\n";
	for(int i = 0; i < DELAY * 10; i++);

	cout << "Philosopher " << n << " eating ...\n";
	for(int i = 0; i < DELAY; i++);
    }

    return n;
}

int main()
{
    cout << "The Dining-Philosophers Problem\n";

    Thread * phil[5];
    for(int i = 0; i < 5; i++)
	phil[i] = new Thread(&philosopher, i);

    int status;
    for(int i = 0; i < 5; i++) {
	phil[i]->join(&status);
	if(status == i)
	    cout << "Philosopher " << i << " went to heaven!\n";
	else
	    cout << "Philosopher " << i << " went to hell!\n";
    }

    return 0;
}


01/10 a 08/10: Laboratório: programação concorrente

Exercício 1

O programa abaixo cria 5 threads, e cada uma destas threads atualiza uma variável global (memória compartilhada).

#include <iostream>
#include "thread.h"

#define NUM_THREADS 5

using namespace std;

int saldo = 1000;

int AtualizaSaldo(int n)
{
	int meu_saldo = saldo;
	int novo_saldo = meu_saldo + n*100;
	cout << "Novo saldo = " << novo_saldo << endl;
	saldo = novo_saldo;
}

int main()
{
	Thread * threads[NUM_THREADS];

	for(int t = 0; t < NUM_THREADS; t++)
		threads[t] = new Thread(&AtualizaSaldo, t+1);

	int status;
	for(int t = 0; t < NUM_THREADS; t++) {
		threads[t]->join(&status);
		cout << "Thread " << t << " terminou com status " << status << "." << endl;
	}

	cout << "Saldo final é " << saldo << "." << endl;
}


  1. Compile este programa. Você precisará da classe Thread implementada na aula passada.
  2. Execute este programa várias vezes. Ele funciona? Será que ele gera as saídas esperadas?
  3. Identifique as seções críticas do programa.
  4. Corrija o programa utilizando mutex. Utilize a classe Mutex implementada na aula passada.
  5. Analise a função AtualizaSaldo() com a sua solução. Lembre-se que o uso do mutex implica em apenas uma thread acessar a seção crítica por vez, enquanto outras threads ficam bloqueadas, esperando. Disso vem que, quanto menor o trecho de código entre um lock e um unlock, menos tempo uma thread necessita ficar esperando.
  6. Modifique a função AtualizaSaldo() para reduzir o tempo de lock do programa.
  7. Modifique o programa para usar um semáforo binário ao invés de um mutex em sua solução. Utilize a classe Semaphore da aula passada.

Exercício 2

O programa abaixo manipula uma matriz de tamanho MxN (veja os defines para o tamanho da matriz). A função SumValues soma todos os valores em uma linha da matriz. A linha a ser somada é identificada pela variável i. Modifique o programa principal (main) nos locais indicados para:

  1. Criar N threads, uma para somar os valores de cada linha.
  2. Receber o resultado do somatório de cada linha e gerar o somatório total da matriz.
  3. Analise o programa: há problemas de sincronização que precisam ser resolvidos? Se sim, resolva-os.
#include <iostream>
#include "thread.h"

/* number of matrix columns and rows */
#define M 5
#define N 10

using namespace std;

int matrix[N][M];
Thread *threads[N];


/* thread function; it sums the values of the matrix in the row */
int SumValues(int i)
{
	int n = i; /* number of row */
	int total = 0; /* the total of the values in the row */
	int j;
	for (j = 0; j < M; j++) /* sum values in the "n" row */
		total += matrix[n][j];
	cout << "The total in row" << n << " is " << total << "." << endl;
	/* terminate a thread and return a total in the row */
	exit(total);
}

int main(int argc, char *argv[])
{
	int i, j;
	int total = 0; /* the total of the values in the matrix */

	 /* initialize the matrix */
	for (i = 0; i < N; i++)
		for (j = 0; j < M; j++)
			matrix[i][j] = i * M + j;

	/* create threads */
	/* COLOQUE SEU CÓDIGO PARA CRIAR AS THREADS AQUI! */

	/* wait for terminate a threads */
	/* COLOQUE SEU CÓDIGO PARA PEGAR O SOMATÓRIO DE LINHAS E TOTALIZAR A SOMA DA MATRIZ AQUI! */

	cout << "The total values in the matrix is " << total << endl;

	return 0;
}


Exercício 3

Corrija os problemas de sincronização do programa abaixo.

#include <iostream>
#include <fstream>
#include <uuid/uuid.h>
#include <unistd.h>
#include <errno.h>

#include "thread.h"

using namespace std;

const int PRODS = 100;
const int REP = 10;

const int CONS = 20;

const int BUF_SIZE = 35;

Thread * prods[PRODS];
Thread * cons[CONS];

uuid_t buffer[BUF_SIZE];
static int prod_pos = 0;
static int cons_pos = 0;

bool finished = false;

int producer(int n)
{
	cout << "Producer was born!" << endl;

	int rep = REP;

	char fname[36+1];

	while(rep--)
	{
		if(++prod_pos == BUF_SIZE) prod_pos = 0;
		uuid_generate(buffer[prod_pos]);
		uuid_unparse(buffer[prod_pos], fname);

		string name(fname,sizeof(uuid_t)*2 + 4);
		ofstream file(name.c_str());
		file << name;
		file.close();
	}

	exit(REP);
}

int consumer(int n)
{
	cout << "Consumer was born!" << endl;

	char fname[36+1];
	int consumed = 0;


	while(true)
	{
		if(finished) exit(consumed);

		consumed++;

		if(++cons_pos == BUF_SIZE) cons_pos = 0;
		uuid_unparse(buffer[cons_pos], fname);

		{
			ifstream file(fname);
			if(!file.good()) continue;
			string str;
			file >> str;
			cout << "Consumed: " << str << endl;
		}

		if(remove(fname)) cerr << "Error: " << errno << endl;
	}

	exit(consumed);
}

int main()
{
    cout << "Massive Producer x Consumer Problem\n";

    // Create
    for(int i = 0; i < PRODS; i++)
    	prods[i] = new Thread(&producer, i);
    for(int i = 0; i < CONS; i++)
    	cons[i] = new Thread(&consumer, i);

    // Join
    int status = 0;
    int produced = 0;
    int consumed = 0;
    for(int i = 0; i < PRODS; i++)
    {
    	prods[i]->join(&status);
    	produced += status;
    }

    finished = true;

    for(int i = 0; i < CONS; i++)
    {
    	cons[i]->join(&status);
    	consumed += status;
    }

    cout << "Total produced: " << produced << endl;
    cout << "Total consumed: " << consumed << endl;

    return 0;
}

Utilize o script abaixo para executar o programa. O script cria um diretório em /tmp/test onde são salvos os arquivos criados pelos produtores. Para ter certeza de que todos os arquivos foram adequadamente consumidos (i.e., deletados), o diretório /tmp/test deve estar vazio após a execução do programa.

#!/bin/bash

export myrun_DIR=`pwd`
export myrun_TEST=/tmp/test

rm -rf $myrun_TEST
mkdir $myrun_TEST
cd $myrun_TEST
$myrun_DIR/multi_prod_cons
cd $myrun_DIR

Problemas de sincronização a serem considerados:

  1. Limite de tamanho do buffer: os produtores só podem produzir se houver espaço no buffer - nunca pode haver mais que BUF_SIZE arquivos na pasta de troca de arquivos.
  2. Uso da posição do buffer: verificar se a posição consumida já foi de fato produzida.
  3. Consumo: verificar se todos os arquivos produzidos foram consumidos.
  4. Sistema de arquivos: verificar se os arquivos estão sendo sincronizados adequadamente no sistema de arquivos (ver man 2 sync).


09/10: Laboratório: programação concorrente

15/10: Encaminhamento para palestra MCC

16/10: Revisão e correção de listas de exercícios

22/10: Prova 1 - Comunicação e coordenação de processos

23/10: Gerenciamento de memória: Introdução

29/10 e 30/10: Gerenciamento de memória

Atividade prática

Escreva um programa de computador que, dada a configuração de um sistema de paginação e um endereço de entrada, forneça informações sobre o endereço dado no referido sistema. Mais detalhes abaixo:

  • Entradas do programa:
    • Largura do endereço em bits
    • Tamanho das páginas em bytes
    • Arquivo com tabela de páginas
    • Endereço a ser traduzido
  • Saídas do programa:
    • Número de frames no sistema
    • Número da página (endereço lógico)
    • Número do frame (endereço físico) (Hit ou Page Fault?)
    • Deslocamento

A tabela de páginas estará em um arquivo em modo texto contendo um mapeamento por linha, como o abaixo. Observe que o arquivo contém os números de página ou frame, e não endereços.

0-10
1-9
2-20
3-37
4-1
5-4
6-7
7-6

A classe abaixo pode ser utilizada para processar o arquivo de entrada. Ela utiliza um map da STL para fazer o mapeamento. Você precisará implementar o método get_frame desta classe.

/*
 * MapFile.h
 *
 *  Created on: Oct 29, 2014
 *      Author: arliones
 */

#ifndef MAPFILE_H_
#define MAPFILE_H_

#include <string>
#include <map>
#include <fstream>
#include <stdlib.h>
#include <iostream>

using namespace std;

class MapFile {
	MapFile() {}
public:
	MapFile(string filename)
	{
		ifstream file(filename.c_str());
		string line;
		char line_c[256];
		unsigned int page, frame, delimiter;
		while(!file.eof()) {
			file.getline(line_c,256); line = string(line_c);
			delimiter = line.find('-');
			page = atoi(line.substr(0,delimiter+1).c_str());
			frame = atoi(line.substr(delimiter+1).c_str());
			_pt.insert(make_pair(page,frame));
		}

	}

	virtual ~MapFile() {}

	// Returns the number of the frame or -1 if not found
	unsigned int get_frame(unsigned int page)
	{
		//TODO
	}

	void print_page_table()
	{
		cout << "Page Table:" << endl;
		map<unsigned int, unsigned int>::iterator mit = _pt.begin();
		for(; mit != _pt.end(); ++mit)
			cout << mit->first << " - " << mit->second << endl;
	}

private:
	map<unsigned int, unsigned int> _pt;
};

#endif /* MAPFILE_H_ */

Abaixo, exemplos de execução do programa desejado:

arliones@socrates:~/workspace/paging_sim$ ./paging_sim
Usage: ./paging_sim ADDR_LEN PAGE_SIZE MAP_FILE ADDRESS

arliones@socrates:~/workspace/paging_sim$ ./paging_sim 16 1024 page_table.txt 5687
Frames in the system: 64
Requested page: 5
Requested frame: 4
Offset: 567

arliones@socrates:~/workspace/paging_sim$ ./paging_sim 16 1024 page_table.txt 10578
Frames in the system: 64
Requested page: 10
Requested frame: Page Fault
Offset: 338

arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4096 page_table.txt 8632
Frames in the system: 1048576
Requested page: 2
Requested frame: 20
Offset: 440

arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4096 page_table.txt 68723
Frames in the system: 1048576
Requested page: 16
Requested frame: Page Fault
Offset: 3187

arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4194304 page_table.txt 354
Frames in the system: 1024
Requested page: 0
Requested frame: 10
Offset: 354

arliones@socrates:~/workspace/paging_sim$ ./paging_sim 32 4194304 page_table.txt 43554432
Frames in the system: 1024
Requested page: 10
Requested frame: Page Fault
Offset: 1611392

Dicas: utilize variáveis unsigned do tipo adequado em seus cálculos. Você provavelmente precisará das funções log2 e exp2 da libmath (include math.h).

12/11 e 13/11: Memória Virtual

19/11 e 20/11: Sistemas de arquivos


26/11: Revisão para prova

  • Dúvidas / Lista de exercícios.
  • Aula será no

27/11: P2 (gerenciamento de memória)

03/12: Sistemas de arquivos: estudos de caso

04/12: Gerenciamento de entrada e saída: introdução / Atividade em laboratório: construção de módulo para Linux

10/12: Revisão e correção de listas de exercícios

11/12: P3 (sistemas de arquivo e gerenciamento de entrada e saída)