Gerenciamento de Memória

Documentos relacionados
Notas da Aula 17 - Fundamentos de Sistemas Operacionais

Hardware (Nível 0) Organização. Interface de Máquina (IM) Interface Interna de Microprogramação (IIMP)

Arquitetura de Computadores. Sistemas Operacionais IV

Sistemas Operacionais

4 Estrutura do Sistema Operacional Kernel

Sistema Operacional Correção - Exercício de Revisão

Sistemas Operacionais

ESTUDO DE CASO WINDOWS VISTA

Notas da Aula 15 - Fundamentos de Sistemas Operacionais

Sistemas Operacionais

Prof. Marcos Ribeiro Quinet de Andrade Universidade Federal Fluminense - UFF Pólo Universitário de Rio das Ostras - PURO

Sistemas Operacionais Aula 06: Threads. Ezequiel R. Zorzal

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 05 Estrutura e arquitetura do SO Parte 1. Cursos de Computação

IFPE. Disciplina: Sistemas Operacionais. Prof. Anderson Luiz Moreira

Sistemas Operacionais

Esta dissertação apresentou duas abordagens para integração entre a linguagem Lua e o Common Language Runtime. O objetivo principal da integração foi

SISTEMAS OPERACIONAIS CAPÍTULO 3 CONCORRÊNCIA

SISTEMAS OPERACIONAIS

Tabela de Símbolos. Análise Semântica A Tabela de Símbolos. Principais Operações. Estrutura da Tabela de Símbolos. Declarações 11/6/2008

11/3/2009. Software. Sistemas de Informação. Software. Software. A Construção de um programa de computador. A Construção de um programa de computador

Conceitos de Sistemas Operacionais: Chamadas de Sistema. Prof Rafael J. Sandim

Capacidade = 512 x 300 x x 2 x 5 = ,72 GB

Gerenciamento de Memória

Sistemas Operacionais. Prof. André Y. Kusumoto

Sistemas Operacionais Gerência de Dispositivos

Orientação a Objetos


Unidade 13: Paralelismo:

Programação Concorrente Processos e Threads

Deadlocks. Prof. Marcos Ribeiro Quinet de Andrade Universidade Federal Fluminense - UFF Pólo Universitário de Rio das Ostras - PURO

SISTEMAS OPERACIONAIS ABERTOS Prof. Ricardo Rodrigues Barcelar

Algoritmos e Programação (Prática) Profa. Andreza Leite andreza.leite@univasf.edu.br

Recursos. Um recurso é ou um dispositivo físico (dedicado) do hardware, ou Solicitar o recurso: esperar pelo recurso, até obtê-lo.

BC Sistemas Operacionais Sistema de Arquivos (aula 10 Parte 2) Prof. Marcelo Z. do Nascimento

UNIVERSIDADE FEDERAL DO PARANÁ UFPR Bacharelado em Ciência da Computação

Memória Virtual. Prof. Dr. José Luís Zem Prof. Dr. Renato Kraide Soffner Prof. Ms. Rossano Pablo Pinto

Unidade VI. Validação e Verificação de Software Teste de Software. Conteúdo. Técnicas de Teste. Estratégias de Teste

Análises Geração RI (representação intermediária) Código Intermediário

Desenvolvimento de um Simulador de Gerenciamento de Memória

Curso Tecnológico de Redes de Computadores 5º período Disciplina: Tecnologia WEB Professor: José Maurício S. Pinheiro V

Gerenciamento de memória. Carlos Eduardo de Carvalho Dantas

Processos e Threads (partes I e II)

Processos (Threads,Virtualização e Migração de Código)

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064

Bancos de dados distribuídos Prof. Tiago Eugenio de Melo

Arquitetura de Sistemas Operacionais Machado/Maia. Arquitetura de Sistemas

Sistemas Operacionais Processos e Threads

Aula 3. Sistemas Operacionais. Prof: Carlos Eduardo de Carvalho Dantas

Fundamentos de Sistemas Operacionais

Disciplina: Sistemas Operacionais - CAFW-UFSM Professor: Roberto Franciscatto

Arquiteturas RISC. (Reduced Instructions Set Computers)

ISO/IEC 12207: Gerência de Configuração

Everson Scherrer Borges João Paulo de Brito Gonçalves

Conceitos de Banco de Dados

UNIVERSIDADE FEDERAL DE SANTA MARIA CENTRO DE TECNOLOGIA AULA 14 PROFª BRUNO CALEGARO

8 Threads. 8.1 Introdução

Modelos. Comunicação com clientes

Sistemas Operacionais

BARRAMENTO DO SISTEMA

Notas da Aula 4 - Fundamentos de Sistemas Operacionais

Métodos de Sincronização do Kernel

Sistema de Arquivos. Ambientes Operacionais. Prof. Simão Sirineo Toscani

Figura 01 Kernel de um Sistema Operacional

BACHARELADO EM SISTEMAS DE INFORMAÇÃO EaD UAB/UFSCar Sistemas de Informação - prof. Dr. Hélio Crestana Guardia

Arquitetura de Computadores. Introdução aos Sistemas Operacionais

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064

Sistemas Operacionais

Introdução à Programação de Computadores

Sistemas Distribuídos Processos I. Prof. MSc. Hugo Souza

Banco de Dados Aula 1 Introdução a Banco de Dados Introdução Sistema Gerenciador de Banco de Dados

ADDRESS RESOLUTION PROTOCOL. Thiago de Almeida Correia

Sistemas Distribuídos

O hardware é a parte física do computador, como o processador, memória, placamãe, entre outras. Figura 2.1 Sistema Computacional Hardware

Sistemas Operacionais. Prof. André Y. Kusumoto

Desenvolvendo Websites com PHP

Entendendo como funciona o NAT

Sistemas Distribuídos

Capítulo 3. Avaliação de Desempenho. 3.1 Definição de Desempenho

Prof.: Roberto Franciscatto. Capítulo 1.2 Aspectos Gerais

Funções de um SO. Gerência de processos Gerência de memória Gerência de Arquivos Gerência de I/O Sistema de Proteção

Introdução aos Computadores

Organização e Arquitetura de Computadores

ARQUITETURA DE COMPUTADORES

Gerenciamento de memória

Escalonamento no Linux e no Windows NT/2000/XP

Sistemas Operacionais

Sistemas Operacionais. Roteiro. Hardware. Marcos Laureano

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 05 Estrutura e arquitetura do SO Parte 2. Cursos de Computação

Estruturas do Sistema de Computação

SISTEMAS OPERACIONAIS. Maquinas Virtuais e Emuladores

UNIVERSIDADE FEDERAL DE SANTA CATARINA UFSC DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA INE BACHARELADO EM CIÊNCIAS DA COMPUTAÇÃO.

6 - Gerência de Dispositivos

Armazenamento Secundário. SCE-183 Algoritmos e Estruturas de Dados II

Programação de Sistemas

Documento de Projeto de Sistema

Modelo Cascata ou Clássico

O que veremos nesta aula? Principais Aspectos de Sistemas Operacionais. Visão geral de um sistema computacional

Sistemas Operacionais

Aula 26: Arquiteturas RISC vs. CISC

Transcrição:

Seminário SO 1 Sistemas Operacionais 18 de novembro de 2008 Gerenciamento de Memória Implementação de Sistemas Operacionais Contemporâneos. Eduardo Felipe Castegnaro Juliano Krieger Heloisa Simon

Seminário SO 2 Sumário Introdução 3 A chamada brk 3 Funcionamento do malloc() 4 Gerenciamento de Memória em Sistemas sem MMU 5 A Solução POSIX para aquisição de memória. 6 Modelos de memória 9 Algoritmos de Alocação de Memória 11 Referências 15

Seminário SO 3 Introdução Motivados pelo interesse em saber o funcionamento de alocações de memória em sistemas operacionais modernos, decidimos abordar neste seminário sobre as funcionalidades da chamada break e seu desempenho em sistemas com memória virtual, solução POSIX, assim como os modelos de memórias e os algoritmos de alocação de memória usadas realmente na prática e não os algoritmos apresentados em livros clássicos. A chamada brk The brk and sbrk functions are historical curiosities left over from earlier days before the advent of virtual memory management BSD System Calls Manual A system call brk() define o fim do segmento de dados, ou seja, controla um grande pedaço de memória contíguo alocada para um determinado processo, que pode ser aumentado ou diminuído posteriormente. Esse controle é permitido apenas para uma das extremidades, pois a Heap cresce em apenas em uma direção (do menor para o maior endereço). É sempre possível expandir, desde que haja espaço no endereçamento. Porém encolher pode ser custoso e ineficiente. Basta que apenas um byte no final deste espaço esteja sendo usado para tornar impossível o encolhimento deste área, mesmo que todos os outros bytes antes deste estejam livres.

Seminário SO 4 Esta chamada de sistema depende de um layout de memória. Ela pode ser utilizada em um processo particular no Unix mas pode não ser usada em outros ambientes e pode ser difícil de emular. Além disso, programas que precisam de chamadas tão diretas à memória são muito raros. Até 2003, Os sistemas Linux Kernel, Debian entre outros estavam vulneráveis, pois não limitavam o endereço passado como parametro na chamada brk(), podendo assim qualquer processo ter acesso a área de código do kernel, permitindo um atacante local ter acesso a privilégios de root. O padrão POSIX (Portable Operating System Interface) é uma família de normas que tem o objetivo de garantir a portabilidade do código-fonte de um programa a partir de um sistema operacional que atenda as normas POSIX, desta forma as regras atuam como uma interface entre sistemas operacionais distintos. Por estas e outras razões citadas acima, a chamada brk(), mesmo sendo utilizada em sistemas UNIX, não faz parte do padrão POSIX, que almeja a padronização e melhor desempenho dos Sistemas Operacionais, atributos esses que seriam impossíveis com o brk(). Funcionamento do malloc() Quando um processo precisa de mais memória, aloca-se mais espaço usando o system call brk() ou sbrk(), como citado acima. Em termos de uso da CPU, uma system call é cara. Uma boa estratégia seria chamar a brk() para pegar um grande pedaço de memória para então separá-la em pedaços menores de acordo com o necessário.

Seminário SO 5 Isso é exatamente o que o malloc() faz. Ele agrega um série de chamadas malloc() menores afim de diminuir a grande chamada brk(). Isso gera uma melhora significativa no desempenho. O malloc() chamando a ele mesmo é muito mais barato do que o brk(), porque é uma chamada de biblioteca e não uma chamada de sistema. Um comportamento similar é adotado quando a memória é liberada pelo processo. Os blocos de memória não são imediatamente devolvidos para o sistema, que chamaria o brk() com um argumento negativo, em vez disso, a biblioteca C agrega essas chamadas até sejam suficientemente grande para serem liberadas tudo de uma só Gerenciamento de Memória em Sistemas sem MMU A implementação de memória virutal em um sistema operacional é fortemente baseado na arquitetura do processador, mais especificamente, na MMU (Memory Management Unit), um hardware dedicado e acoplado com o processador, que suporta endereçamento para memória virtual. Em Sistemas embarcados, é possível encontrar arquiteturas onde não há uma MMU (Memory Management Unit). Para estes sistemas a memória virtual tem de ser emulada por software, Dentro vários existentes, três são mostrados em detalhes abaixo: VM pura imita um hardware com MMU. Todo acesso a memória está em um espaço de endereçamento virtual que é traduzido para endereço físico em tempo de processamento. Este método é transparente para a aplicação. No entanto, todo acesso a memória virtualizada resulta em uma chamada a função de mapeamento de memória virtual física, tantas vezes quanto forem necessárias instruções de load/store no programa. VM com endereço fixo neste método, uma região da memória é marcada como virtualizada. Qualquer acesso à memória (load/store) que pertence a esta região é transfor-

Seminário SO 6 mada. Neste método é necessário que o programador indique ao vm-assembler a região marcada como virtual. Em oposiçao ao VM puro, neste caso, o overhead de transformar todo endereço virtual em endereço físico é reduzido apenas à região de memória marcada como virtual. Assim também exige que, em tempo de execuçao, todo acesso a memória seja checado, para conferir se está marcado. VM Seletiva Similar ao método anterior, mas um pouco mais refinada, em termos de determinar quais trexos da memória estão virtualizados. No caso anterior, uma checagem em tempo de execução era precisa a cada acesso á memória. VM Seletiva evita a checagem em tempo de execução anotando as estruturas de dados que são virtualmente alocadas no código-fonte. Necessita que o progamador marque as estruturas como pertencentes ao espaço de endereçamento virtual (em vez da região inteira). Esta anotação é feita na declaraçao da variável, usando diretivas. Toda vez que a variável é usada, o código gerado é modificado para chamar a função de mapeamento de memória virtual para memória física. Este método reduz significantemente o overhead em tempo de execução restringindo o mapeamento apenas às grandes estruturas de dados que se beneficiam da virtualização. Este método dá ao programador mais controle em o que está sendo virtualizado. Todavia, é o menos transparente ao programador, comparado com os dois outros métodos. A Solução POSIX para aquisição de memória. O maior problema de chamadas como brk() é a exigência de layouts específicos e contíguos de memória dentro de um address space. Sendo assim foi necessário criar novas chamadas que não possuem esse requisito.

Seminário SO 7 Após considerar várias outras alternativas, a chamada mmap(), inicialmente encontrada no System V, release 4 (SVR4), foi decidida para adoção pelo padrão POSIX. A definição original é minima, no sentido que apenas descreve o que já havia sido implementado e o que aparenta ser necessário para um mapeador genérico e portátil. Mesmo mmap() tendo sido originalmente desenvolvido para mapear arquivos em memória, na realidade ele é um mapeador de uso geral, podendo mapear em memória qualquer coisa, como dispositivos, arquivos, e inclusive a própria memória, através do conceito de mapeamento anônimo. Mapeamento anônimo ocorre quando páginas da memória são mapeadas mas não pertencem a nenhum recurso, ou seja, elas servem apenas para reservar um determinado pedaço dentro do address space do processo. Memória mapeada tem a vantagem que inicialmente apenas o address space é modificado, com cada página mapeada efetivamente reservada no primeiro acesso, permitindo uma melhor utilização em casos onde preemptivamente é necessário alocar buffers grandes, mas eles não necessariamente serão utilizados. Caso comum em momentos de instanciação de aplicações. Chamadas individuais de mmap não necessariamente pertencem a blocos contíguos no espaço de endereçamento, mas é garantido que em um bloco mapeado todos os endereços serão contíguos. Isso causa grande flexibilidade, pois é possível mapear pedaços grandes para casos específicos e manter um buffer de mapeamento pequeno, tornando muito eficiente o uso de memória dos programas. Opcionalmente, é possível mapear páginas com proteção, para leitura, escrita, ou execução. Esse mapeamento é dependente de hardware, e não necessariamente garantido. Também é possível mapear de maneira pública permitindo fácil comunicação entre processos que mapearam o mesmo pedaço. Efetivamente é assim que chamadas para alocação de memória compartilhada (shr_malloc) são implementadas. É possível mapear

Seminário SO 8 páginas anônimas que possuem primitivas de sincronização, utilizando a flag MAP_HAS- SEMAPHORE. A função mmap estabelece uma ligação entre o espaço de endereçamento do processo e a memória, ligada por n bytes a partir do endereço retornado pela função. Quando um mapeamento é realizado, é possível que a implementação necessite mapear mais que o solicitado, visto que o mapeamento ocorre apenas com páginas inteiras. Mesmo assim aplicativos não devem contar com esse comportamento. Implementações que não usam memória por paginação podem simplesmente alocar um pedaço de tamanho variável, não causando o comportamento de páginas discretas. Se um programa aplicativo solicita um mapeamento em um address space previamente mapeado, seria desejável que a implementação detectasse isso, e informasse a aplicação. Entretanto, foi especificado que não é possível remapear pedaços já mapeados. Muitas implementações não sequem esse padrão, pois seria muito caro solicitar uma chamada para desmapear o endereço antes de cada chamada para mapeá-lo. Na prática novos mapeamentos sobrescrevem mapeamentos antigos, no mesmo endereço, liberando os antigos para reuso pelo sistema operacional. Mesmo sendo um alocador genérico, caso seja solicitado um endereço em intervalo reservado ao kernel ele irá falhar, pois se trabalha sob a presunção que o kernel gerencia sua própria memória através de outros mecanismos. Da mesma maneira caso não haja suporte nativo para endereços virtuais, intervalos fixos devem ser definidos. A especificação suporta múltiplos tipos de mapeamentos, que implementações podem escolher suportar ou não. Implementações como do BSD são conhecidamente mais conservadoras, não suportando chamadas com grande custo, como com sincronização implícita.

Seminário SO 9 A chamada mmap permite uma maior otimização de operações de paginação pelo Sistema Operacional. Considere, por exemplo um programa A que cria um buffer de 1MB em memória não mapeada (usando brk), e um programa B que mapeia 1MB (utilizando mmap). Se o sistema operacional tiver que enviar parte da memória de A para swap ele precisa escrever o conteúdo do buffer no swap antes de reutilizar a memória. No caso de B as páginas podem ser reutilizadas automaticamente, porque o SO sabe restaurá-las da area de swap automaticamente. Para realizar isso sistemas operacionais como MacOS e BSD originalmente mapeiam páginas como apenas de leitura e tratam a interrupção da MMU quando os processos nela escrevem, detectando assim páginas sujas. Algumas implementações de mmap(), especificamente a pertencente ao Darwin (base do MacOS), possuem bugs relativos a devolver páginas não utilizadas e manter o address range reservado pela chamada original. É possível liberá-las utilizando munmap(), mas essa é uma chamada custosa e pode causar condições de corrida caso haja algum acesso a memória durante a chamada de munmap(). O comportamento observado é que paginas mapeadas que não foram escritas são automaticamente capturadas pelo sistema operacional, mas páginas mapeadas ocupam espaço até serem desmapeadas. Já que alocadores reusam espaço, geralmente todas as páginas mapeadas acabam tendo sido escritas em algum ponto, dificultando a captura das mesmas. Modelos de memória O propósito de um modelo de memória é descrever como um leituras e escritas em memória pode ser executadas por um processador, de maneira relativa a sua ordem no programa original e como escritas feitas por um processador ficam visíveis a outros pro-

Seminário SO 10 cessadores. Ambos aspectos afetam profundamente as otimizações dependentes de hardware permitidas ao compilador bem como toda a estrutura de cache e gerenciamento de memória via hardware (MMU). Uma solução passível de adoção deveria ser rígida o suficiente para facilitar a progamabilidade e flexível o suficiente para permitir a reorganização de certos acessos a memória, facilitando a otimização. Devido a difícil tarefa de definir tal modelo originalmente linguagens de sistema, como C e C++, não possuem um modelo de memória que leve em consideração o multiprocessamento ou o compartilhamento de memória. O modelo utilizado não é consistente, e tende a ser o que quer que tenha sido definido pela combinação particular de compilador e hardware existente em uma determinada plataforma. Isso causa problemas, pois atualmente programadores não podem, de maneira consistente e multiplataforma, escrever código lock-free, uma vez que otimizações podem introduzir leituras ou escritas inexistentes no código original, e consequentemente não podem ser asseguradas pelo programador. O modelo existente possui as seguintes características: Especifica o comportamento de operações de memória observáveis pelo programa atual. Não possui noção implícita de memória compartilhada entre processos, consequentemente não especifica parâmetros de acesso concorrente. É extremamente flexível em acessos a memória, permitindo grandes otimizações pelo compilador e processador. Não determina regras de acesso a memória compartilhada entre threads do mesmo processo. O novo standard de C++, também conhecido como C++0x, contém suporte explícito a threads na linguagem, obrigando a definição de um modelo de memória consistente. Há

Seminário SO 11 duas partes envolvidas na solução atualmente proposta: A definição de um modelo que permita multiplas threads coexistirem em um programa, e o fornecimento de bibliotecas de suporte que permirtam a criação e o sincronismo de múltiplas threads de execução. Também foi proposto um sistema de atomicidade relativa a tipos nativos em C++, definidos pela palavra reservada atomic. Tipos atômicos garantem que operações neles executadas não possuam sua representação reordenada por compiladores ou processadores. Pós incremento, pré-incremento e operações com referência ao próprio valor, como soma-ao-valor (+=) também tem a garantia de atomicidade. Sendo assim podemos esperar que futuramente novas bibliotecas possam surgir que permitam a criação de programas eficientes, multiplataformas, em linguagens de sistema. Há indicações no comitê ISO que o próximo Standard da linguagem C siga uma abordagem similar a do C++0x. Algoritmos de Alocação de Memória Conforme visto em aulas, os sistemas mais comuns de alocação de memória involvem o gerenciamento de um segmento contínuo de memória de tamanho arbitrário. A família de algoritmos mais comum para esse caso é conhecida como ʻbuddy algorithmsʼ que involve a fusão de segmentos livres contínuos e sua separação duarante a alocação. Claramente é disperdicio se mapear uma página para a utilização de alguns bytes, pois se subutiliza a memória e se executa chamadas complexas de sistema. Uma solução comum é se alocar um pedaço contíguo e gerenciar o seu uso, mas isso tente a causar fragmentação interna, ou ainda a solução adotada for versões anteriores a 2.4 do Kernel do Linux, que consistia em prover segmentos de memória geometrica-

Seminário SO 12 mente distribuidos, ou seja, seu tamanho depende de potências de 2, ao invés do tamanho que é realmente necessário. Fica claro que rodar um sistema de alocação de memória em cima desses algoritmos não é particularmente eficiente. Podemos criar subsistemas que utilizam internamente algoritmos Buddy, aumentando a eficiência, como demostrado a seguir para o kernel 2.6 O algoritmo implementado nas ultimas versões do Kernel Linux são derivadas de um algoritmo conhecido como Slab Allocator. Esse algoritmo aloca objetos em cestos, conhecidos como slabs, conforme os seguintes quesitos: O tipo de dados a ser guardado pode afetar a maneira de alocado. As áreas de memória podem ser vistas como objetos, cada um dentro de um cache. A tendência é solicitar áreas de memória do mesmo tipo consecutivamente. Por exemplo na criação de processos é necessária a alocação de tabelas, como o descritor de

Seminário SO 13 processo. Uma vez que o processo acaba essa tabela não é dealocada, mas sim utilizada como cache para futuras utilizações. Aumenta a localidade de dados, visto que objetos dentro da mesma cache podem ser acessados frequentemente, espalhando em linhas de cache separadas. Outra grande vantagem desse algoritmo é que cada slab pode ser mapeado separadamente, fazendo dele excelente escolha em sistemas POSIX. Estrutura do alocador Slab. Internamente cada Slab consiste em uma area sequencial de memória, gerenciada via Buddy. Opcionalmente os slabs podem ser com descritores internos ou externos.

Seminário SO 14

Seminário SO 15 Referências BOVET, Daniel. Understanding the Linux Kernel. 3a Edição. O Reilly, Nov. 2005. BOEHM, Hans-J. C++ Atomic Types and Operations, Postado em 10/09/2007 Disponível em: <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html> Acesso em 17/11/2008.