Esse documento representa parte dos meus estudos acerca do Livro - Operational Systems Three Easy Pieces, de dominio publico.


Um processo representa um programa em execucao e seus respectivos componentes. O programa armazenado em si, nao representa nada alem de instrucoes e algum tipo de dado estatico, ele so passa a ter algum valor de fato quando executado pelo SO. Neste contexto, cria-se uma problematica. Se um programa so pode ser validado ao ser executado, como podemos executar varios programas em apenas uma maquina fisica, a qual tem apenas uma unidade de processamento?

podemos utilizar da virtualizacao da CPU. A tecnica para executar varios programas é conhecida como Compartilhamento de tempo da CPU. Esta tecnica permite executarmos processos de forma concorrente, com apenas uma pequena perda de performance, ja que a CPU estara sendo compartilhada.

Utilizamos Mecanismos, que sao metodos e protocolos low level que implementam funcionalidades do sistema. Um exemplo de mecanismo: Context Switcher, que permite a pausa e intercalacao da execucao de programas.

Abstraindo processos

Um processo, evidentemente, é apenas uma abstracao dada a um programa, o qual acessa e/ou modifica partes do sistema. Para entendermos um processo, precisamos entender primeiramente o que sao os Estados de Maquina: todos os componentes o qual o programa consegue alterar. A exemplo, o estado da memoria. As instrucoes de um programa se compreendem dentro da memoria, assim como todo dado armazenado. Desta forma, o address space faz parte da construcao de um processo, juntamente aos registrados que participam das operacoes (por exemplo o program counter ou o stack pointer).

Process API

Uma API de processo deve poder:

  • criar: invocado ao se executar qualquer programa (gerar um PID, virtualizar memoria e gerenciar o lifecycle e context)
  • destruir: matar o processo (kill)
  • esperar: apenas permitir a execucao do processo e sua finalizacao.
  • controle variado: suspensao e resumo do processo, e.g.
  • status: devolver info sobre o estado do processo.

Criacao de um processo

O primeiro passo é o carregamento de memoria (o codigo e variaveis inicializadas) para a memoria principal e, consequentemente, para os registradores necessarios. Este carregamento pode ser feita de 2 formas: a forma “pensa num bicho ansioso”, o qual o programa é inteiramente carregado, sem injecao ou associacao de dependencias, ou, da maneira preguicosa (Lazy Loading), a qual carregamos o programa e suas dependencias durante o tempo de execucao, baseado na necessidade de utilizar x ou y dep. Cobriremos mais estes topicos quando falarmos de tecnicas como paginacao e swapping.

O segundo passo é criar uma run-time stack, ou seja, alocar memoria para o programa poder armazenar informacoes locais como variaveis, system calls ou qualquer outra coisa. O processo tambem pode inicializar a stack com, por exemplo, args externas, passadas em C pelo argc e argv, e.g.

Tambem é alocado um espaco da heap caso seja explicitamente chamado, ou podemos setar qualquer metodo de alocacao.

Podemos tambem criar tags, como no linux, file descriptors, que permitem input, output e descricoes de erro de forma simplificada.

Quando finalizado o carregamento, o SO passa o controle via um mecanismo para a rotina principal do programa executado (main() em C).

Estados do processo

  • Running: executando as instrucoes.
  • Ready: esta pronto, mas nao executando as instrucoes, por algum motivo.
  • blocked: quando o processo precisa de alguma resposta de outro evento, e.g, I/O calls.

A alteracao de um processo pode ser feita via Scheduling, onde um processo é Scheduled para executar as instrucoes (running state) e descheduled para esperar qualquer coisa.

Estrutura de Dados

Como um programa, o SO precisa de estruturas de dados padrao para mapear algumas atividades. Por exemplo, para mapear os estados dos processos, pode-se criar uma process list que registra todos os processos no estado ready e/ou running.

Podemos ver um exemplo de structs/registros que representam o contexto de registradores


// the registers xv6 will save and restore
// to stop and subsequently restart a process
struct context {
    int eip;
    int esp;
    int ebx;
    int ecx;
    int edx;
    int esi;
    int edi;
    int ebp;
};
// the different states a process can be in
enum proc_state { UNUSED, EMBRYO, SLEEPING,
    RUNNABLE, RUNNING, ZOMBIE };
// the information xv6 tracks about each process
// including its register context and state
struct proc {
    char *mem; // Start of process memory
    uint sz; // Size of process memory
    char *kstack; // Bottom of kernel stack
    // for this process
    enum proc_state state; // Process state
    int pid; // Process ID
    struct proc *parent; // Parent process
    void *chan; // If !zero, sleeping on chan
    int killed; // If !zero, has been killed
    struct file *ofile[NOFILE]; // Open files
    struct inode *cwd; // Current directory
    struct context context; // Switch here to run process
    struct trapframe *tf; // Trap frame for the
    // current interrupt
}

Este contexto de registradores pode ser alterado pela tecnica de context switch.

Bibliografia

https://pages.cs.wisc.edu/~remzi/OSTEP/cpu-intro.pdf