Mudanças entre as edições de "SOP29005-2014-1"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
Linha 288: Linha 288:
 
=> Roteiro do Prof. Maziero: http://dainf.ct.utfpr.edu.br/~maziero/doku.php/unix:shell_basico
 
=> Roteiro do Prof. Maziero: http://dainf.ct.utfpr.edu.br/~maziero/doku.php/unix:shell_basico
  
=== P0: Biblioteca de Filas ===
+
=== 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.
 
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.

Edição das 16h45min de 23 de fevereiro de 2014

EngTel: Sistemas Operacionais - Diário de Aulas 2014-1

Professor: Arliones Hoeller
Turma: 29005
Encontros: terças e sextas às 9:40.
Atendimento paralelo: segundas às 13:30 e quintas às 8:25.
Endereço web do grupo: https://www.facebook.com/groups/sop29005.ifsc.sj/
Endereço de e-mail da disciplina: sop29005.ifsc.sj@groups.facebook.com

Outros cursos de sistemas operacionais nos quais este curso se baseia:

Plano de ensino

Cronograma de Trabalhos Práticos

Trabalho Descrição Lançamento Entrega
t0 Biblioteca de filas 21/02/2014 14/03/2014
t1 Troca de contexto 28/02/2014 21/03/2014
t2 Biblioteca de tarefas 28/02/2014 21/03/2014
t3 Despachante de tarefas 07/03/2014 28/03/2014
t4 Escalonamento por prioridade 07/03/2014 28/03/2014
t5 Preempção e compartilhamento de tempo 14/03/2014 04/04/2014
t6 Contabilização de tarefas 14/03/2014 04/04/2014
t0 - t6 Apresentação dos trabalhos - 04/04/2014
t7 Tarefa main 15/04/2014 06/05/2014
t8 Operador join 15/04/2014 06/05/2014
t9 Chamada sleep 15/04/2014 06/05/2014
tA Semáforo 25/04/2014 16/05/2014
tB Uso de semáforos 13/05/2014 06/06/2014
tC Fila de mensagens 13/05/2014 06/06/2014
t7 - tC Apresentação dos trabalhos - 06/06/2014
tD Gerente de disco (trabalho de recuperação) 10/06/2014 01/07/2014

Diário de Aulas

11/02: Apresentação da disciplina. Histórico, visão geral e estruturas de um SO

  • Revolution OS: documentário sobre Linux
  • Apresentação sobre histórico visão geral e estruturas básicas de um SO. [slides]

14/02: Finalização do conteúdo anterior e atividades em laboratório: Introdução ao Linux e GCC/G++

18/02: Mais sobre desenvolvimento em C++ com Linux – Herança, métodos e atributos estáticos, bibliotecas

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:
    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.


21/02: Finalização do conteúdo anterior e atividades em laboratório

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.

DoublyCircularlyLinkedList.png

Para recapitular a execução de operações sobre uma lista encadeada, consulte o material de 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 Queue.h e Queue.cc. A declaração da classe é apresentada abaixo:

/*
 * 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_ */

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 Queue_Test.cc, apresentado abaixo:

/*
 * 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;
};
 
int main() {
    cout << "Welcome to BOOOS - Basic Object Oriented Operating System!" << endl;
    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;
}

É 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 aqui.

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é 07/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.