menu_book Índice

Logo UFPR

Tarefas, Processos e Threads

Logo Licenciatura

DEE355 – Sistemas Operacionais

Prof. Jéferjefer@ufpr.br

docs.ufpr.br/~jefer

EAD – Moodle: ava.ufpr.br

Explore os fundamentos e práticas relacionadas a tarefas, processos e threads em sistemas operacionais modernos.

Slides: Versão Antiga

Logo UFPR

Tarefas: Conceitos

Logo Licenciatura

Tarefa: Execução de uma sequência de instruções em linguagem de máquina, normalmente gerada pela compilação de um programa escrito em qualquer linguagem.

  • Tarefa vs. Programa: O programa é o conjunto estático de instruções, enquanto a tarefa é a execução dinâmica com estado, contexto e interação.
  • Multiplexação: Técnica que permite ao processador alternar entre várias tarefas, utilizando preempção. Essa abordagem possibilita que o tempo do processador seja distribuído de forma eficiente entre diversas tarefas, permitindo a execução de múltiplas tarefas quase simultaneamente em um único núcleo, o que é fundamental para sistemas multitarefa modernos.
Logo UFPR

Sistemas Monotarefa

Logo Licenciatura

Características (Anos 40):

  • Uma tarefa por vez, carregada do disco para memória;
  • Execução contínua até a conclusão, sem interrupção (Maziero, Cap. 3);
  • Gerenciamento sequencial de dados de entrada/saída.
Diagrama de Estados Monotarefa
Logo UFPR

Tempo Compartilhado

Logo Licenciatura

Sistemas Multitarefa Preemptivos:

  • CTSS (1961): Pioneiro no time-sharing (Corbató, 1963);
  • Quantum: Intervalo de tempo por tarefa (ex.: 10-200ms no Linux – Tanenbaum, Cap. 2);
  • Preempção: Suspende e retoma tarefas conforme necessidade (Silberschatz, Cap. 5).
Dinâmica da Preempção
Logo UFPR

Ciclo de Vida das Tarefas

Logo Licenciatura

Estados (Maziero, Cap. 3):

  • Nova: Tarefa sendo criada;
  • Pronta: Aguardando a alocação do processador;
  • Executando: Em execução;
  • Suspensa: Aguardando evento ou recurso;
  • Terminada: Concluída.
Ciclo de Vida
Logo UFPR

Troca de Contexto

Logo Licenciatura

Definição: Salvar e restaurar o contexto de uma tarefa (TCB – Task Control Block) para permitir a alternância entre tarefas.

  • TCB: Armazena registradores, contador de programa, etc. (Maziero, Cap. 3);
  • Curiosidade: No Linux para Intel x86, as operações de troca de contexto estão definidas em Assembly no arquivo arch/i386/kernel/process.c dos fontes do núcleo.
Troca de Contexto
Logo UFPR

Processos

Logo Licenciatura

Processo: Uma tarefa associada a recursos (memória, arquivos, etc.) – Silberschatz, Cap. 3.

  • Evolução: Historicamente, 1 tarefa = 1 processo; atualmente, um processo pode conter múltiplas tarefas.
  • PCB: O Process Control Block armazena o estado e os recursos do processo (Maziero, Cap. 3).
PCB
Logo UFPR

Comandos Linux

Logo Licenciatura

Comandos para Gerenciamento de Processos

  • ps -aux: Lista todos os processos em execução, com informações como PID, usuário, uso de CPU e memória.
  • pstree: Mostra a hierarquia de processos em formato de árvore, destacando processos pais e filhos.
  • top: Interface interativa em tempo real que mostra os processos mais ativos. Pressione q para sair.
  • htop: Versão visual melhorada do top (pode exigir instalação com sudo apt install htop).
  • jobs: Lista tarefas em segundo plano no terminal atual (ex: após usar & ou Ctrl+Z).
  • fg / bg: Traz uma tarefa para o primeiro plano (fg) ou envia para segundo plano (bg).
  • kill PID: Envia um sinal para encerrar um processo. Exemplo: kill 1234.
  • kill -9 PID: Força a finalização do processo usando o sinal SIGKILL.
  • killall nome: Encerra todos os processos com determinado nome. Ex: killall firefox.
  • pkill -f nome: Encerra processos com base em expressões. Ex: pkill -f python.
  • nice -n valor comando: Executa um processo com prioridade ajustada (quanto menor, mais prioridade). Ex: nice -n 10 ./meuprog.
  • renice -n valor -p PID: Altera a prioridade de um processo já em execução.
  • fuser -n tcp 80: Mostra o PID de processos que usam a porta 80 (exemplo comum: servidor web).
  • nohup comando &: Executa um processo que continua mesmo após logout. Ex: nohup ./servidor &
  • echo $$: Mostra o PID do shell atual.
  • ps -o pid,ppid,comm -p $$: Mostra o PID e o PPID (processo pai) do shell.
  • wait: Aguarda a finalização de um processo filho antes de continuar a execução (útil em scripts).

🧪 Experimente:
Execute ps -aux, pstree, top ou htop no terminal.
Use sleep 60 & para simular um processo em segundo plano e manipule com jobs, fg, kill.

🔎 Visualizando Threads

  • ps -T -p PID → mostra threads de um processo
  • top -H -p PID → visualização por thread
  • ls /proc/PID/task/ → lista threads no kernel

Dica: Use um programa com múltiplas threads (ex: Python) e observe essas diferenças.

Threads

Thread: Fluxo de execução dentro de um processo, com sua própria pilha e registradores, mas que compartilha os recursos do processo (Silberschatz, Cap. 4).

  • User Threads (N:1): Mapeadas em uma única thread de kernel;
  • Kernel Threads (1:1): Gerenciadas diretamente pelo SO (ex.: Linux, Windows);
  • N:M: Modelo híbrido (ex.: Solaris, FreeBSD KSE);
  • PThreads: Padrão POSIX para threads em C (Tanenbaum, Cap. 2).
Modelos de Threads

Importante: Threads compartilham memória → podem causar problemas de concorrência (race conditions).


Animação: Criação de Threads

Processo (PID único)

Processo vs Threads

Processos

Memória A
Memória B
Memória C

Isolados

Threads

Memória Compartilhada
T1
T2
T3

Compartilham recursos

Threads no Linux (na prática)

No Linux, cada thread possui um identificador próprio (TID) e pode ser visualizada como um processo leve (LWP).


Exemplo Prático: Processo com Múltiplas Threads

Programa em Python que cria múltiplas threads dentro de um único processo:

import threading
import time
import os

def worker(id):
    while True:
        for _ in range(10000000):  # pequena carga de CPU
            pass
        
        print(f"Worker {id} | PID: {os.getpid()} | TID: {threading.get_native_id()}")
        time.sleep(1)

for i in range(3):
    threading.Thread(target=worker, args=(i,), daemon=True).start()

while True:
    time.sleep(10)

Observação: Um único processo (PID) cria várias threads (TIDs diferentes).


Como identificar o PID do programa

Forma 1 (recomendada): pgrep

pgrep -f threads.py
  • Retorna diretamente o PID
  • Mais limpo e preciso

Forma 2 (visual): htop

htop
  • Pressionar F3
  • Digitar python
  • Observar a coluna PID

Forma 3 (background): $!

python3 threads.py &
echo $!
  • Retorna o PID do último processo em background

Forma 4 (alternativa): pidof

pidof python3
  • Retorna todos os processos python
  • Cuidado: não distingue facilmente o script

Fluxo da Demonstração

python3 threads.py
pgrep -f threads.py
ps -T -p PID
top -H -p PID
ls /proc/PID/task/

Interpretação:
- ps -T mostra quantas threads existem
- top -H mostra como cada thread usa CPU em tempo real

Logo UFPR

Prática: Monitoramento e Manipulação

Logo Licenciatura

Visualização e Controle de Processos

  1. Listar Processos:
    ps -aux
  2. Exibir Árvore de Processos:
    pstree
  3. Gerenciar Jobs:
    sleep 60 &
    jobs, fg, bg
  4. Ajustar Prioridade:
    nice -n 10 sleep 60
    renice para processos já em execução
  5. Verificar PID e PPID:
    echo $$
    ps -o pid,ppid,comm -p $$

Identificar Processos Usando Portas com fuser

Inicie um servidor local:

sudo python3 -m http.server 80
    

Em outro terminal:

sudo fuser -n tcp -v 80
    

Finalizar processo da porta:

sudo fuser -k -n tcp 80
    
Diagrama de Estados de um Processo

Script em Shell

#!/bin/bash
for i in {1..5}
do
  echo "Processo em execução: $i"
  sleep 1
done
    

Programa com Threads (Python)

import threading
import time
import os

def worker():
    while True:
        print("PID:", os.getpid(), "TID:", threading.get_native_id())
        time.sleep(2)

for i in range(3):
    threading.Thread(target=worker, daemon=True).start()

while True:
    time.sleep(10)

Execute e depois use:
ps -T -p PID
top -H -p PID

Programa com Threads (C)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

void *thread_func(void *arg) {
    int id = *(int*)arg;
    for (int i = 0; i < 5; i++) {
        printf("Thread %d executando: %d\n", id, i);
        sleep(1);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t t1, t2;
    int id1 = 1, id2 = 2;
    pthread_create(&t1, NULL, thread_func, &id1);
    pthread_create(&t2, NULL, thread_func, &id2);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;
}
    

Monitoramento de Processo (C)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    pid_t pid = getpid();
    printf("PID do processo: %d\n", pid);
    fflush(stdout);

    while(1) {
        sleep(1);
    }

    return 0;
}
    

Compilação e Execução

gcc -o meu_programa meu_programa.c
./meu_programa
ps -ef | grep meu_programa
pgrep meu_programa
Ctrl + Z   → bg   → fg
nohup ./meu_programa &
    

Finalizar Processos com kill

  • Finalizar via PID: kill <PID>
  • Forçar finalização: kill -9 <PID>
  • Por nome: killall meu_programa
  • Via top: pressione k dentro do top
  • Usando htop (visual): selecione e pressione F9
  • Com pkill: pkill -f meu_programa
Logo UFPR

Mini-Quiz

Logo Licenciatura
Logo UFPR

Bibliografia

Logo Licenciatura

Básica

- Silberschatz et al., Fundamentos de SOs, 8ª ed., LTC, 2010.
- Tanenbaum, SOs Modernos, 3ª ed., Prentice Hall, 2009.
- Marques et al., Sistemas Operacionais, 1ª ed., LTC, 2011.

Complementar

- Maziero, SOCM
- Toscani et al., Sistemas Operacionais, 4ª ed., Bookman, 2010.
- Silberschatz et al., SOs com Java, 7ª ed., Campus, 2008.