Sie sind auf Seite 1von 20

Introdução

O que é um sistema operacional?

• um provedor de abstrações
• um coordenador de recursos
• um mágico: faz com que seu sistema pareça mais do que na realidade ele é (mais
de um processador, memória maior)

A principal tarefa do SO é "adaptar" o hardware.

Exemplos: MS-DOS/Windows, MacOS, UNIX.

O que vem incluído em um SO?

Já que pouquíssimas pessoas projetam e escrevem sistemas operacionais, para que


estudá-los?

• conceitos de sistemas operacionais são relevantes em toda ciência da


computação. Um entendimento dos conceitos de sistemas operacionais fornece
uma excelente base para a construção de sistemas de software complexos
• entender o SO é fundamental para um entendimento profundo de um sistema de
computação. Isto é útil mesmo que você nunca vá escrever um SO.

Uma História Social de Sistemas Operacionais


Fase 0: Computadores são uma ciência experimental e exótica: não precisa de
sistema operacional

• computador programado através de "plugs" no painel


• usuário presente todo o tempo; toda atividade é sequencial: nenhuma
sobreposição entre computação, E/S e tempo de pensar do usuário
• conjuntos de cartões manualmente carregados para executar os programas
• eventualmente foram desenvolvidas bibliotecas, utilizadas por todos (...
precursoras dos sistemas operacionais)

Problema: muita espera.

• usuário tem que esperar pela máquina


• máquina tem que esperar pelo usuário
• todos têm que esperar pela leitora de cartões

Fase 1: Computadores são caros; pessoas são baratas

• torna utilização do computador mais eficiente, desacoplando as atividades das


pessoas das atividades do computador
• SO funciona como um monitor batch, continuamente carregando um job,
executando e continuando com o próximo job. Se o programa falhasse, o SO
salvava uma cópia do conteúdo de memória para o programador depurar (1955-
1965)
• melhor utilização do hardware; bem difícil para depurar!
• "canais de E/S" e interrupções: sobrepõe E/S e computação
o uso de "buffers" e gerenciamento de interrupções feito pelo SO
o "spool" de jobs para o tambor magnético

Problemas:

• só um usuário de cada vez na máquina


• usuário tem que esperar pela máquina
• um SO para cada máquina

Técnicas de hardware: adiciona proteção à memória e relocação

• multiprogramação: muitos usuários podem compartilhar o sistema


• jobs pequenos podem completar rapidamente
• SO passa a ter que gerenciar interação entre jobs concorrentes

IBM OS/360: primeiro SO projetado para uma família de computadores

• utilizar o mesmo SO em todas as máquinas, da mais simples à mais complexa


• SO passa a ser um assunto de estudo em ciência da computação
• SOs passaram a ser estudados porque eles não funcionavam. OS/360 é um
exemplo: anunciado em 1963, só funcionou realmente em 1968

Novos problemas:

• SOs extremamente complicados


• todo escrito em assembler, milhares de linhas (e de erros)
• usuários ainda esperando pela máquina; isso motivou a fase 2

Fase 2: Computadores são rápidos; pessoas são lentas; ambos são caros. Necessário
tornar as pessoas mais produtivas.

"Timesharing" interativo: permitir que vários usuários utilizem a mesma máquina


simultaneamente

• um terminal para cada usuário


• manter os dados "on-line": utilização de sistemas de arquivos estruturados
• prover tempo de resposta razoável

Fase 3: Computadores são baratos; pessoas são caras. Dar um computador para
cada pessoa.

• workstation pessoal:
o SUN (Stanford University Network)
o Xerox Alto
• Apple II
• IBM PC
• MacIntosh

Fase 4: Computadores Pessoais (PCs) invadem o planeta

• fornecedores de software produzem programas para uso individual de alta


qualidade com suporte limitado para funções de SO
• proteção e segurança são de importância secundária
• redes possibilitam aparecimento de novas aplicações importantes: e-mail,
bboards, newsgroups, WWW. Aparecimento de arquiteturas cliente-servidor.
• máquinas servidoras motivam mais avanços na tecnologia de SOs

Problemas:

• as pessoas ainda continuam esperando por computadores


• viruses e worms
• hackers

História Técnica de Sistemas Operacionais


Sistemas Timesharing

CTSS (1962):

• MIT
• um dos primeiros sistemas timesharing
• motivou o desenvolvimento do MULTICS

MULTICS (1965):

• projeto conjunto entre MIT, Bell Labs e General Eletric


• objetivo era um "distribuidor" de serviços de computação. As pessoas iriam
comprar esses serviços do mesmo modo que compram eletricidade
• introduziu várias idéias fundamentais: anéis de proteção, sistema de arquivos
estruturado em árvore e outros
• construir foi mais difícil do que era esperado

UNIX (1970):

• Ken Thompson (do projeto MULTICS) descobriu um PDP-7 sem utilização no


Bell Labs
• ele e Dennis Ritchie construiram um sistema projetado por programadores para
programadores
• primeira versão em assembler, depois em C
• código fonte foi disponibilizado para Universidades
• Berkeley adicionou suporte para memória virtual
• UNIX se tornou um SO comercial
• idéias importantes popularizadas pelo UNIX:
o SO escrito numa linguagem de alto nível
o SO portável
o sistema de arquivos estruturado em árvore
o várias outras

PCs e Workstations

• CP/M: primeiro SO para PCs


• IBM precisava de software para seu novo PC; liberação do CP/M estava
atrasada
• IBM contrata Microsoft (Bill Gates, BASIC) para construir um SO
• objetivo principal: terminar rápido e ser compatível com CP/M

Hoje: redes

• sistemas de computação são compostos de recursos pessoais e recursos


compartilhados
• pessoas não querem compartilhar CPU e memória, mas querem compartilhar
correio eletrônico, bancos de dados, programas e outros

Sistemas Operacionais Hoje


Os SOs atuais são:

• Enormes:
o se o kernel do seu SO ocupa menos que 1 Mbyte, ele é considerado
pequeno
o tipicamente centenas de milhares de linhas de código
o 100-1000 homens-ano de esforço de desenvolvimento
• Complexos:
o assíncronos
o idiossincrasias de hardware (tem que executar em qualquer plataforma)
o classes diferentes de usuários apresentam diferentes demandas
o performance é crucial
• Mal compreendidos:
o os sistemas duram mais que seus criadores
o muito grandes para uma só pessoa compreender
o nunca estão completamente livres de erros (OS/360 foi liberado com
1000 "bugs")
o comportamento é difícil de prever; ajustes de performance feitos, em
geral, baseados em intuição; intuição quase sempre está errada

Conceitos em SOs:

• system calls (chamadas de sistema)


• processos
• interrupções

Funcionalidade de SOs
Concorrência: usuário compartilha memória, dispositivos de E/S e processadores; no
entanto cada usuário imagina que tem sua própria máquina

• um usuário pode executar múltiplas atividades


• SO utiliza a abstração processo para gerenciar as interações
• SO deve evitar que os processos interajam de maneira errônea ou não-
intencional

Suporte a E/S: dispositivos de E/S são lentos; é desejável que a CPU não fique ociosa
durante operações de E/S. Motivação para interrupções.

• dispositivos operam independentemente


• CPU fornece os comandos para os dispositivos e vai executar outra coisa
• quando a operação completa, o dispositivo interrompe a CPU
• CPU interrompe o que estava fazendo, trata a interrupção, e depois continua
com o que estava fazendo
• SO deve garantir que interrupções não pertubem o estado corrente da máquina
• em algumas situações deve-se gerenciar interrupções múltiplas

Memória: usuários/processos têm que compartilhar uma quantidade limitada de


memória.

• SO tem que coordenar esse compartilhamento


• disponibiliza memória real quando um processo está executando, através de
paginação ou segmentação
• programas são escritos como se a máquina tivesse uma memória muito maior

Sistema de Arquivos: arquivos não podem desaparecer e são pessoais

• SO tem que alocar espaço em discos e fitas


• SO tem que prover proteção

Redes: usuários podem se comunicar com outras máquinas ou podem utilizar outras
máquinas

• SO tem que suportar protocolos de comunicação, naming, transparência na rede,


sistemas de arquivos remotos
• SO tem que fazer um sistema distribuído se parecer com um sistema
centralizado

O SO apresenta ilusões aos usuários:

• cada programa executa na sua própria máquina


• existe mais memória do que você jamais vai precisar
• eventos podem acontecer simultaneamente
• (... quase sempre o SO é bem sucedido)
Processos (Tanenbaum 2.1)
Em sistemas operacionais modernos, vários programas executam concorrentemente. A
abstração ``processo'' ajuda a manter o sistema organizado.

O que é um Processo?

Um processo é uma abstração utilizada para representar um programa em execução. Um


processo contem toda informação necessária para completar uma computação.

Em alguns sistemas processos são chamados de tarefas (``tasks'').

Um processo é a mesma coisa que um programa?

Não. Um processo é, ao mesmo tempo, mais e menos que um programa.

• Mais: o programa é apenas parte do estado. Se duas pessoas estão executando o


programa ls , elas estão usando apenas um programa, mas existem dois
processos.
• Menos: programas podem gerar vários processos. Por exemplo, quando você
executa um make , são executados um compilador, um assembler, um
carregador, entre outras coisas.

Que informações são necessárias para o sistema gerenciar um processo?

• a sequência de instruções que compõe o programa


• o valores correntes dos dados do programa
• o endereço da instrução que está sendo executada
• os arquivos que estão abertos
• Exemplo: o Unix utiliza, entre outras, as estruturas proc e user para manter o
estado de um processo

Porque duas estruturas?

A estrutura proc é utilizada para manter informações que têm que ficar na memória
durante toda a vida do processo. A estrutura user contem o restante do estado do
processo, e pode ser paginada para o disco.

Uniprogramação e Multiprogramação

• sistemas uniprogramados permitem a execução de apenas um processo por vez.


Exemplo: MS/DOS.
• sistemas multiprogramados (ou multitarefa) permitem mais de um processo.
Exemplos: Unix, Windows NT.
• algumas vezes a distinção não é clara. Exemplos: MacOS, Windows.
• escrever um SO uniprogramado é muito mais fácil; não é necessário se
preocupar com interação entre múltiplos processos
• multiprogramação permite melhor utilização dos recursos da máquina, além de
permitir a execução de mais de um programa ao mesmo tempo

Estruturas de dados para a gerência de processos

As estruturas user e proc mantêm estado de software independente da máquina. O


sistema tem que manter também estado de hardware dependente da máquina. Por
exemplo, o Unix utiliza uma estrutura chamada process control block (pcb). Ela pode
ser encontrada, tipicamente, em /usr/include/machine/pcb.h. Veja abaixo o arquivo
pcb.h da máquina topazio (rodando SunOS 4.1.2):

/* @(#)pcb.h 1.2 90/08/06 SMI */

/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/

#ifndef _sun4m_pcb_h
#define _sun4m_pcb_h

/*
* Sun software process control block
*/

#include

#define MAXWIN 12 /* max number of windows currently


supported */

/*
* The system actually supports one more than the above number.
* There is always one window reserved for trap handlers that
* never has to be saved into the pcb struct.
*/

#ifndef LOCORE
#include
struct pcb {
/* this struct MUST be 1K aligned -- checked for in assym.s */
struct l1pt pcb_l1pt; /* level 1 page table */
label_t pcb_regs; /* saved pc and sp */
int pcb_psr; /* processor status word */
int pcb_uwm; /* user window mask */
struct rwindow pcb_wbuf[MAXWIN]; /* user window save buffer
*/
char *pcb_spbuf[MAXWIN]; /* sp's for each wbuf */
int pcb_wbcnt; /* number of saved windows in pcb_wbuf
*/

int *pcb_psrp; /* psr pointer to en/disable


coprocessors */
struct fpu *pcb_fpctxp;/* pointer to fpu state */
int pcb_fpflags; /* fpu enable flags */
int *pcb_cpctxp; /* pointer to coprocessor state */
int pcb_cpflags; /* coprocessor enable flags */

int pcb_flags; /* various state flags */


int pcb_wocnt; /* window overflow count */
int pcb_wucnt; /* window underflow count */
};

#define pcb_pc pcb_regs.val[0]


#define pcb_sp pcb_regs.val[1]

#define aston() {u.u_pcb.pcb_flags |= AST_SCHED;}

#define astoff() {u.u_pcb.pcb_flags &= ~AST_SCHED;}

#endif !LOCORE

/* pcb_flags */
#define AST_SCHED 0x80000000 /* force a reschedule */
#define AST_CLR 0x80000000
#define PME_CLR 0
#define AST_NONE 0

/* pcb_flags */
#define CLEAN_WINDOWS 0x1 /* keep user regs clean */
#define FIX_ALIGNMENT 0x2 /* fix unaligned references */

#endif /*!_sun4m_pcb_h*/

Como vários processos podem compartilhar uma única CPU?

O SO tem que assegurar que os processos não interfiram uns com os outros. Isto
significa:

• garantir que todos os processos tenham acesso aos recursos, como memória e
CPU: escalonamento
• controlar o estado dos processos, de tal modo que um processo não possa
modificar arbitrariamente o estado de outro processo: proteção

Estados de um processo

A qualquer instante, um processo pode estar em um dos seguintes estados (Fig. 2-2):

• executando: utilizando a CPU de fato


• pronto (``ready''): executável; temporariamente parado enquanto outro processo
executa
• bloqueado: não-executável até que um evento externo aconteça

O SO deve gerenciar as transições dos processos entre esses estados: escalonador


(scheduler ou dispatcher).

De onde surgem os processos?

De outros processos. O sistema Unix tem uma primitiva (system call) fork que cria uma
cópia do processo existente. Por exemplo, um shell Unix cria uma cópia dele mesmo
cada vez que você quer executar um programa. Uma cópia espera enquanto a outra
executa o comando. Como o fork funciona:

• garante que o processo que executou o fork não está executando


• garante que todo o estado do processo que executou o fork foi salvo
• faz uma cópia do código, dados e pilha
• copia o PCB do processo original no processo novo
• torna o novo processo conhecido para o escalonador

Problema: os dois processos são idênticos. Como fazer para executar um programa
diferente?

• exec : troca a imagem do programa corrente com o código e dados do novo


programa

Um probleminha adicional: como é que tudo isso começa? No início não existe nenhum
processo, portanto não é possível executar um fork .

Threads e Espaços de Endereçamento


Computações múltiplas compartilham um processador único através de processos. A
abstração ``processo'' tem dois objetivos gerais:

1. permitir ao SO organizar e controlar múltiplas computações


2. prevenir processos de interferir uns com os outros

Um processo pode ser imaginado como tendo dois componentes, cada um


correspondendo a uma das necessidades acima:

1. Thread
2. Espaço de Endereçamento

Ou seja, o componente Espaço de Endereçamento é utilizado para implementar


isolamento, e o componente Thread é utilizado para implementar controle. O
componente Espaço de Endereçamento inclui:

• a memória utilizada pelo programa; isto inclui o código e grande parte dos dados
• arquivos abertos

O componente Thread inclui:

• contador de programa
• registradores
• dados alocados na pilha (na maioria variáveis locais)
• endereços de retorno dos procedimentos ativos

Se olharmos o pcb :

• o Espaço de Endereçamento é representado pela tabela de páginas (pcb_l1pt)


• o estado da Thread é representado pelo resto

A maioria das estruturas proc e user são consideradas parte do Espaço de


Endereçamento.

Um processo "normal" (por exemplo ls ou cat em Unix) consiste de uma Thread e de


um Espaço de Endereçamento.

Threads múltiplas em um único Espaço de Endereçamento podem ser utilizadas para


implementar concorrência dentro de um processo. Imagine que você tem um processo
que serve requisições de I/O (por exemplo, um servidor de arquivos ou um sistema de
bancos de dados):

• quando uma thread solicita uma leitura, ela fica bloqueada até que os dados
fiquem disponíveis
• um único processo com uma única thread não pode executar nada de útil
enquanto o disco está sendo acessado
• com threads múltiplas, uma thread pode continuar a executar enquanto a outra
espera pelo I/O

Threads compartilham:

• memória
• arquivos abertos
• variáveis globais

Threads mantêm cópias privadas de:

• pilhas
• contador de programa
• estado do processador

Threads são também chamadas de processos leves (lightweight processes), porque:

• criação de threads é mais barato que criação de processos


• threads têm menos estado que processos, podendo ser gerenciadas de maneira
mais eficiente
• comunicação entre threads é mais rápida/fácil do que comunicação entre
processos

Diferentes sistemas operacionais apresentam diferentes modelos:

• um espaço de endereçamento, uma thread


• um espaço de endereçamento, múltiplas threads
• múltiplos espaços de endereçamento, uma thread por espaço de endereçamento
• múltiplos espaços de endereçamento, múltiplas threads por espaço de
endereçamento
Escalonamento de Processos

(Tanenbaum 2.4)

Estados de um Processo

• running
• blocked
• ready
• rever Fig. 2-2

Recursos

• entidades ("objetos") sobre as quais processos operam


• ex.: tempo de CPU, espaço em disco
• podem ser:
o preemptáveis
o não-preemptáveis
• decisões sobre recursos
o alocação
o escalonamento

Escalonamento: aspectos de projeto

• escalonador e algoritmo de escalonamento


• critérios a satisfazer:
o "fairness" (justiça)
o eficiência
o minimizar tempo de resposta
o minimizar "turnaround"
o maximizar "throughput"
• escalonamento preemptivo vs. não-preemptivo

FIFO (First-In-First-Out)

• processos executam até o fim


• resulta (geralmente) em uniprogramação
• alto grau de justiça
• problemas?
Round-Robin

• cada processo tem um quantum de tempo para executar


• fatores a considerar
o tamanho do quantum
o troca de contexto
• problemas?

Escalonamento com Prioridade

• atribuição de prioridades diferentes a cada processo


• processo mais prioritário roda primeiro
• atribuição de prioridades pode ser:
o estática
o dinâmica
• problemas?

Filas Múltiplas

• classses de prioridades
• objetivos:
o aumentar a eficiência
o tratar diferentes tipos de processos, cada qual com suas
necessidades
• problemas?

Shortest Job First

• projetado para sistemas nos quais tempos de execução são


conhecidos a priori
• minimiza tempo de resposta médio
• problemas?

Escalonamento Garantido

• procura garantir um nível de performance mínimo para o usuário


• objetivo: dar a todos processos a mesma quantidade de CPU
• se existem N processos, cada um recebe 1/N de tempo da CPU
• tem que manter história de utilização da CPU para cada processo
• se um processo utilizou menos que sua parte, sua prioridade é
aumentada
• se um processo utilizou mais que sua parte, sua prioridade é
diminuida
• dá uma prioridade maior aos processos que utilizaram menos CPU
num passado recente
• "jobs" altamente interativos (ex.: editor de textos) utilizam pouca
CPU, portanto ganham alta prioridade
• "jobs CPU-bound" (ex.: simulações científicas) ficam com prioridade
baixa
• formas alternativas de escalonamento garantido são necessárias em
sistemas de tempo real
• problemas?
Escalonamento em Dois Níveis

• utilizado quando é necessário mover processos (executáveis) entre


memória e disco
• nível mais alto decide quais processos ficam na memória e quais vão
para o disco
• nível mais baixo decide qual processo, dentre os que estão na
memória, vai utilizar a CPU
• problemas?

Política vs. Mecanismo

• necessidade de separar os mecanismos de escalonamento das


políticas de escalonamento
• algoritmo de escalonamento é parametrizado de alguma maneira
• processos dos usuários definem os parâmetros

Sumário

• algoritmos de escalonamento têm um grande impacto no "overhead"


do sistema, na sua eficiência e no tempo de resposta de processos
interativos
• os melhores esquemas são adaptativos; para ser ótimo é necessário
prever o futuro
• os melhores algoritmos de escalonamento tendem a dar maior
prioridade aos processos que utilizam menos recursos

Memória: Utilização e Alocação

Utilização da Memória
Informação armazenada na memória pode ser classificada de várias
maneiras. Alguns exemplos:

Papel em linguagens de programação:

• instruções: especificam as operações a serem executadas e seus


operandos
• variáveis: a informação que muda à medida que o programa executa:
locais, globais, parâmetros de procedimentos, memória dinâmica
• constantes: informação que é utilizada como operando, mas nunca
muda; exemplo: pi, printf("esse string e constante\n");

Proteção:

• modificável
• leitura-apenas ("read-only")
• executável

Inicialização

Endereço ou Dado

Tempo de Ligação ("Binding Time"):

• em que momento o espaço é alocado?


• em que momento deve-se decidir sobre o que vai aonde?
• existem duas alternativas:
o alocação estática: decisões são tomadas uma vez, e para
sempre, antes do programa começar a executar. Pode
acontecer em tempo de compilação, tempo de ligação ("link
time") ou tempo de carga ("load time")
o alocação dinâmica: decisões não podem ser tomadas antes do
tempo de execução ("run time"), e podem mudar

Note que as classificações podem se sobrepor: por exemplo, variáveis


podem ser estáticas ou dinâmicas; código pode ser "read-only" ou "read-
write".

Quando um processo está executando, qual é o aspecto da memória?

• memória é dividida em áreas chamadas segmentos


• no UNIX, cada processo tem três segmentos:
o código (chamdado "text" na gíria UNIX)
o dados
o pilha

Questões:

• como fazer se existe mais de um processo?


• aonde vai ficar o sistema operacional?

Segmentos

Porque distinguir entre segmentos diferentes?

• podemos querer alguns "read-only", outros "read-write"


• alguns são conhecidos em tempo de compilação, outros crescem ou
diminuem à medida que o programa executa (dados estáticos vs.
pilha)

Alocando memória aos segmentos

Divisão de responsabilidades entre várias partes do sistema:

• Compilador: gera um arquivo-objeto para cada arquivo-fonte


o cria os segmentos de código e dados para o arquivo-fonte
o informação nesse arquivo-objeto está incompleta (porque?)
• Ligador ("linker"): combina todos arquivos-objeto para um programa
em um único arquivo-objeto, que está completo e auto-suficiente
(i.e., todas referências resolvidas)
• Carregador ("loader"): sistema operacional carrega os arquivos-objeto
na memória, permitindo que vários processos diferentes
compartilhem a memória ao mesmo tempo. Deve prover recursos
para que os processos obtenham mais memória depois que eles
tiverem começado a executar
• Tempo de Execução ("run-time"): o ambiente de tempo de execução
funciona junto com o sistema operacional para prover rotinas de
alocação dinâmica de memória, tais como malloc e free em C

Ligadores (por exemplo, ld no UNIX) juntam várias partes separadas de um


programa e reorganizam a alocação de memória. Em geral são considerados
parte do sistema operacional. No UNIX, ld é um programa regular
(usualmente invocado pelo compilador, por exemplo, cc).

Ligadores (Linkers)
Funções de um ligador

• coletar todos os arquivos e bibliotecas de um programa


• descobrir um nova organização de memória de tal modo que todas as
partes fiquem organizadas juntas (combinar segmentos similares:
agrupar segmentos de código; agrupar segmentos de dados)
• ajusta os endereços, de tal modo que o programa possa executar sob
a nova organização de memória

Ligação: desafios

• quando o compilador monta um único arquivo-objeto existem vários


símbolos que ele não conhece (ex.: printf, sin)
o se o compilador não pode identificar o símbolo, assinalar um 0
a ele
o deixa a informação para o ligador
o essa informação é chamada de cross-reference (referência
cruzada)
o referências cruzadas descrevem símbolos em outros arquivos
o uma referência cruzada tem duas partes:
 definição global: um arquivo fornece a variável ou
procedimento que pode ser utilizada por outros arquivos
 referência externa: um arquivo acessa uma variável ou
procedimento que não está presente no arquivo
• compilador não sabe aonde os objetos que ele está compilando serão
colocados na memória
o assume que tudo começa no endereço 0 e deixa para o ligador
consertar
o informação incluída no arquivo-objeto diz ao ligador o que deve
ser re-arranjado/consertado
o essa informação é chamada de informação de relocação; ela
descreve os símbolos no segmento local
Conteúdo dos arquivos-objeto

• Cabeçalho do arquivo: para cada segmento, o arquivo-objeto contem


o tamanho do segmento e seu endereço inicial quando carregado na
memória
• Múltiplos segmentos, para código, dados inicializados, tabelas de
importação etc.
• Tabela de símbolos: uma tabela com uma entrada para cada símbolo
(função, variável etc.) definido no módulo, com informação sobre a
definição do objeto de memória correspondente (endereços, tipo,
tamanho etc.). Essa informação é utilizada para traduzir do nome
para o objeto propriamente dito
• Informação de relocação: informação sobre referências cruzadas que
o ligador tem que consertar:
o onde estão as referências cruzadas?
o a quais símbolos elas se referem?
o como o endereço deve ser arrumado?
1. armazena o endereço do símbolo
2. soma o endereço do símbolo
3. soma a quantia que o símbolo se moveu
o símbolos externos: objetos que não são parte do módulo
corrente
o símbolos internos: o compilador atribuiu um endereço, mas
este endereço pode mudar se o ligador mover algum objeto
• Informação de depuração
• Para mais informação sobre esse assunto, ver livros sobre os formatos
COFF ou PE, ou ler a "man page" do UNIX que descreve o formato do
arquivo a.out

Ligador: funcionamento

Tem que dar vários passos; não é possível terminar sem ter lido todos arquivos.

• Passo 1
o ler toda informação da tabela de símbolos
o decidir como a memória será rearranjada
o ler toda informação de relocação
o descobrir tudo que for necessário nas bibliotecas
• Passo 2
o ler os segmentos e informação de relocação
o modificar os endereços
o escrever o novo módulo com símbolos, segmentos, e relocação

A tabela de símbolos armazena informação sobre os símbolos e, em alguns


casos, sobre os segmentos:

• segmentos: nome, tamanho, endereço


• símbolos: nome, segmento que contem o símbolo, deslocamento
dentro do segmento

Tipicamente, algum algoritmo de "hashing" é utilizado para associar os


nomes às informações.
Ligador: Exemplo
Programa Principal

main ()
{
static float x, val;
extern float sin();
extern printf(), scanf();

printf("Type number: ");


scanf("%f", &x);
val = sin(x);
printf("Sine is %f\n", val);
}

Módulo matemático

float sin(x)
float x;
{
static float tmp1, tmp2, result;
// calcula função seno aqui
return (result);
}

Biblioteca C

printf (...) { ... }

scanf (...) { ... }

Sumário

Passo 1:

• assinala endereços dos segmentos de entrada para poder completar


os segmentos de saída
• nenhuma informação precisa ser carregada na memória nesse ponto,
exceto informações sobre os símbolos

Passo 2:

• lê dados e informação de relocação dos arquivos


• corrige os endereços
• escreve o novo arquivo-objeto

Que tipo de mudanças devem ser feitas em tempo de ligação?

• move r1, @X (X é externo)


• call sin (sin é externo)
• jmp @X (X no mesmo segmento original)
• br x (X no mesmo segmento original, deslocamento curto)

Monoprogramação
Até agora vimos como a memória é vista do ponto de vista do programa do
usuário. A partir de agora vamos ver como o S.O. vê (e gerencia) a
memória.

Todos os processos são ligados assumindo que o endereço inicial é 0. Como carregá-los
na memória? A solução mais simples é utilizar monoprogramação:

• somente um processo por vez na memória


• colocar o S.O. na parte mais alta da memória, num endereço fixo,
conhecido
• colocar o processo no endereço 0, do mesmo modo como ele é ligado
• colocar a pilha logo abaixo do S.O.
• como não existem outros processos na memória, não é possível
corrompê-los (mas é possível corromper o S.O.; tudo vai parar de
funcionar: reboot)
• os primeiros "monitores" batch utilizaram esse sistema; alguns PCs
ainda usam algo parecido
• esse esquema não é aceitável para sistemas "time-sharing"
• aliás, também não é bom para PCs

Então, quais são os objetivos para um bom gerenciamente da memória pelo


S.O.?

• permitir que vários processos co-existam na memória


• nenhum processo deve ter conhecimento deste compartilhamento
(i.e., o mecanismo deve ser transparente)
• todos processos devem executar, independente do número e
posicionamento dos outros processos na memória
• processos devem ser impedidos de corromper outros processos
(proteção)
• o compartilhamento da memória deve ser eficiente; nem a
performance da CPU, nem a da memória devem degradar em função
do compartilhamento (afinal de contas, o objetivo de compartilhar é
aumentar a eficiência)

Multiprogramação com Partições Fixas


Ou: Relocação Estática

• como antes, colocar o S.O. numa posição fixa da memória (por


exemplo, na parte mais alta)
• do mesmo modo que na compilação, temos o problema de não saber
aonde um processo vai residir na memória (se tivermos mais de um
processo na memória ao mesmo tempo)
• do mesmo modo que na compilação, deixamos para "consertar" os
endereços mais tarde
• quando um processo é carregado, "relocar" ele na memória, de
maneira similar ao funcionamento do ligador; todos os endereços
ficam relativos a um ponto fixo na memória

Quais são os problemas?

• nenhuma proteção (eu posso corromper sua memória)


• qualquer um pode corromper o S.O. (e isso vai afetar outros
processos)
• processos podem ser movidos depois que começaram a executar?
• processos podem mudar de tamanho?
• exemplo: IBM/360 OS/MFT

Processos no Unix

Processos no Unix (Tanenbaum 7.3.1)


• processos sequenciais com uma única linha (thread) de controle
• daemons: processos em background que sempre estão executando:
o cron
o mail
o printer
o finger
• FORK: chamada de sistema para criar processos:
o processo parent: FORK retorna PID > 0 (PID do filho)
o processo child: FORK retorna PID = 0
• PID é o identificador do processo
• arquivos abertos são compatilhados
• GETPID: retorna o PID do processo chamador
• uso de FORK pelos filhos possibilita criar uma árvore de processos

Exemplo de uso de FORK e PID:

pid = fork(); /* se o fork tiver sucesso, pid > 0 no pai */

if (pid < 0) {
/* fork falhou, em geral por falta de memoria */
} else if (pid > 0) {
/* codigo do pai entra aqui */
} else {
/* codigo do filho entra aqui */
}

Inicialização do sistema: Figura 7-6

Comunicação entre processos

• é implementada através de troca de mensagens (pipes) e de


• interrupções de software (signal)

Signal

• um processo pode enviar um signal para outro processo


• processo pode informar ao sistema o que fazer se um signal chegar:
o ignorar o sinal
o capturar o sinal (catch)
o deixar o sinal matar o processo (kill)
• se o processo aceitar o sinal ele tem que prover um procedimento de
manipulação do sinal (signal handling procedure)
• um processo só pode enviar um sinal para processos do seu process
group: ascendentes, irmãos e descendentes.

Das könnte Ihnen auch gefallen