2009-12-08

Excepções, parte II (vários tipos de erros)

No último post falei de excepções e das instruções fornecidas por linguagens de alto-nível para as manipular. Neste vou falar das situações em que estas surgem e como devem ser tratadas.

2009-12-02

Excepções, parte I (de códigos de erro a excepções)

Códigos de erro

Em C há muitas funções que devolvem valores especiais para indicar erros. Esta estratégia, embora simples, apresenta vários defeitos:

  1. o tipo de dados de retorno não pode ser usado por completo;
  2. o código que chama estas funções é bastante chato de escrever e difícil de ler (são necessário ifs por todo o lado);
  3. é fácil esquecermo-nos de testar os valores de retorno;
  4. é trabalhoso propagar estes erros manualmente pela stack acima;

2009-12-01

Usar locks explícitas, correctamente

Por vezes synchronized não serve e temos de usar locks explicitamente. No entanto, quando abandonamos a simplicidade de synchronized, também abandonamos as suas garantias de segurança e robustez.

2009-11-05

Variáveis de condição (semáforo)

Por esta altura já devem dominar wait conditions. Por isso vou só elaborar rapidamente o que é um semáforo, e mostrar o código.

Variáveis de condição (BoundedBuffer)

O enunciado deste exercício pede a implementação de um bounded buffer que funcione correctamente com múltiplas threads. Um bounded buffer como o próprio nome indica é um buffer de tamanho fixo. É necessário que depois de cheio as tentativas de adicionar novos dados ao buffer bloqueiem, e o mesmo para tentativas de retirar dados quando este estiver vazio.

2009-10-30

Variáveis de condição (banco)

O primeiro exercício do terceiro guião pede para reimplementar a classe Banco de modo a bloquear as operações que conduzam a saldos negativos. Como o título é “Variáveis de Condição” o que realmente se pretende é que as operações que conduzam a saldos negativos bloqueiem até que seja possível efectuá-las e não que sejam proibidas, para resolvermos este problema com variáveis de condição em lugar de simples ifs.

2009-10-14

Exclusão Mútua (deadlocks)

Agora é-nos pedido para reimplementar o banco com exclusão mútua ao nível das contas individuais.

O que significa isto?

Exclusão Mútua

No último post encontrámos um problema que surge numa situação tão simples como incrementar um contador, simplesmente porque introduzimos paralelismo sem termos controlo sobre esse paralelismo.

2009-10-08

Criação de Threads

Teoria

Em cada instante na nossa máquina existem vários processos a correr em simultâneo. Não importa se temos um único processador/core ou mais que um processador/core. O sistema operativo faz com que os processos corram em simultâneo, ou melhor, com que os processos deêm a impressão de correr em simultâneo. Isto permite que eu escreva este post enquanto ouço música, entre outras coisas.

2009-06-29

Mais Makefiles

Para aqueles que ainda não usam Makefiles (porque “dá muito trabalho”, porque “não sei fazer isso” ou porque “não vou perder tempo”) deixo aqui um template (mais ou menos) genérico para compilar pequenos programas em C.

2009-06-27

Pipelines

…ou em brasileiro: canalizações :)

$ man 7 pipe

Como diz no manual um pipe é um canal de comunicação unidireccional entre dois processos. Um dos processos escreve (com write) numa ponta, o outro lê (com read) na outra.

Redireccionar as standard streams

Para adicionar à shell a possibilidade de redireccionar o standard input e o standard output de e para ficheiros temos de usar a system call dup2:

2009-06-17

Sinais, parte II

Background/Foreground

Até aqui a nossa shell corre sempre os processos em foreground. Isto significa que a shell pára até cada processo terminar. Assim não podemos correr mais que um processo de cada vez.

Testes, exames, essas cenas

Queria pedir desculpa a todos aqueles que me pediram ajuda no domingo por não ter respondido a ninguém. Não tenho net em casa, e por isso só reparei nos comentários bastante tarde. Também tive teste de CG na segunda-feira e por isso só comecei a estudar para SO segunda à tarde. Ainda acabei o código dos guiões que me faltavam, mas só perto da meia-noite. Duvido muito que por essa altura tenham ajudado alguém, mas gostava de pensar que sim.

2009-05-25

Sinais, parte I

Um pequeno aparte

Bem, eu não gosto muito deste guião. A página do manual da system call signal que é apresentada no guião, começa assim:

The behavior of signal() varies across Unix versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead.

Depois de ler isto é óbvio porque é que eu prefiro sigaction. Mas a sigaction é ligeiramente mais complicada.

Ignorando a minha opinião negativa quanto a este guião, vou resolvê-lo com recurso a signal, porque aparentemente é o que os professores esperam.

Execução de programas

A lista de system calls deste guião é deveras assustadora. Não contando com a system, são nada mais, nada menos que sete variantes da mesma função: exec.

2009-05-17

Processos, parte II

Se ainda se lembram, na primeira parte as ocorrências não eram impressas por ordem. Na altura eu toquei muito ao de leve neste assunto. Desta vez vou falar um pouco mais sobre isso, visto esta segunda parte ter como objectivo resolver esse problema.

2009-05-02

Processos, parte I

Background

O objectivo do exercício 3.1 é implementar um programa que inicie vários processos para procurar um número numa matriz. Cada um dos processos deve procurar numa das linhas da matriz. Para isso vamos usar a chamada ao sistema fork.

2009-04-06

Ficheiros, parte II

Na primeira parte ficou por fazer a função copy:

int copy(int fdin, int fdout);

Esta função lê todos os bytes do ficheiro indicado pelo primeiro descritor e escreve-os no segundo.

2009-04-05

Ficheiros, parte I

O primeiro exercício do terceiro guião diz o seguinte: Implemente em C um programa com a funcionalidade do cat. O objectivo é fazer isso, mas sem usar a stdio.h (printf, scanf, etc). No lugar dessas funções temos de usar as chamadas ao sistema que estão no guião.

2009-04-04

Gestor de memória, parte IV

Se ainda se lembram, o nosso gestor de memória já divide blocos grandes em blocos mais pequenos para não desperdiçar memória. Mas esse processo traz um outro problema que também leva a algum desperdício de memória.

2009-03-31

Makefiles

Tenho estado a trabalhar para outras cadeiras e não tenho tido muito tempo para acabar o gestor de memória. Como já tinha este post sobre Makefiles em draft há uns tempos aqui fica...


Já vimos anteriormente alguns usos de um Makefile e também como criar um de forma simples.

Também já mostrei um Makefile com macros e regras automáticas. Na altura não expliquei nada mas prometi que o fazia num post futuro. Vou fazer isso neste post.

2009-03-21

Gestor de memória, parte III

No segundo exercício é pedida uma versão melhorada do gestor de memória do primeiro exercício. São pedidas duas coisas: fusão de blocos livres e optimização das procuras.

2009-03-13

Gestor de memória, parte II

Agora que já temos uma ideia do que temos de fazer podemos começar a escrever o nosso gestor de memória.

Makefile

Vamos dividir o trabalho em três modulos:

  1. mymalloc.o: um módulo com as funções mymalloc e myfree
  2. dict.o: para testar vamos usar uma versão modificada do código do primeiro guião, usando mymalloc e myfree no lugar de malloc e free
  3. prog.o: um módulo com a função main, exactamente igual à do primeiro guião

Gestor de memória, parte I

A Heap

Quando um programa está em memória, há dois segmentos de memória que podem crescer: a heap e a stack.

A stack cresce quando se chamam funções e diminui quando estas retornam. A heap cresce quando alocamos memória dinamicamente.

Quando alocamos memória geralmente fazemo-lo por blocos, dependendo do tamanho das nossas estruturas de dados. Para isto usamos as funções malloc e free.

2009-03-07

Apontadores, parte II

O segundo exercício pode ser facilmente resolvido à custa do primeiro, assim:

void put2(Node** dict, int key, char* val)
{
*dict = put1(*dict, key, val);
}

void del2(Node** dict, int key)
{
*dict = del1(*dict, key);
}

// A função get é exactamente igual
char* get2(Node* dict, int key)
{
return get1(dict, key);
}

Mas vamos fazer as coisas sem "máfias".

Apontadores, parte I

Vou resolver passo a passo o exercício 1 do primeiro guião das aulas teórico-práticas de SO (UM, LEI, 2008-2009).