Destruição de Processos no Kernel de Sistemas Operacionais

No contexto de sistemas operacionais, a destruição de processos é um procedimento crítico gerenciado pelo kernel, envolvendo chamadas de sistema como exit e waitpid. Este processo assegura a liberação adequada de recursos e a comunicação entre processos pai e filho.

Quando um processo é encerrado via exit, o kernel executa uma série de etapas. Inicialmente, a memória associada aos segmentos de código e dados é liberada. Em seguida, todos os arquivos abertos pelo processo são fechados, e as operações de sincronização com o diretório atual e nós i são realizadas. Se o processo possuir filhos, eles são reatribuídos ao processo init (PID 1). Além disso, se o processo for o líder de uma sessão, todos os processos dessa sessão são terminados. O estado do processo é alterado para zumbi, e um sinal SIGCHLD é enviado ao processo pai.

O processo pai normalmente invoca wait ou waitpid para monitorar o término dos filhos. Ao receber SIGCHLD, o pai encerra o processo zumbi, acumula os tempos de execução do filho nas suas próprias variáveis e libera a estrutura de descrição do processo filho, além de limpar a tabela de tarefas e liberar páginas de memória associadas.

Abaixo, exemplos de código reeescritos em C, com alterações na estrutura, nomes de variáveis e lógica, mantendo a funcionalidade original:

int enviar_sinal(int alvo_pid, int sinal) {
    struct tarefa_struct **ptr = tabela_tarefas + NUM_TAREFAS;
    int erro, resultado = 0;
    if (!alvo_pid) {
        while (--ptr > &TAREFA_INICIAL) {
            if (*ptr && (*ptr)->grupo_processo == processo_atual->pid) {
                erro = sinalizar(sinal, *ptr, 1);
                if (erro) resultado = erro;
            }
        }
    } else if (alvo_pid > 0) {
        while (--ptr > &TAREFA_INICIAL) {
            if (*ptr && (*ptr)->pid == alvo_pid) {
                erro = sinalizar(sinal, *ptr, 0);
                if (erro) resultado = erro;
            }
        }
    } else if (alvo_pid == -1) {
        while (--ptr > &TAREFA_INICIAL) {
            erro = sinalizar(sinal, *ptr, 0);
            if (erro) resultado = erro;
        }
    } else {
        while (--ptr > &TAREFA_INICIAL) {
            if (*ptr && (*ptr)->grupo_processo == -alvo_pid) {
                erro = sinalizar(sinal, *ptr, 0);
                if (erro) resultado = erro;
            }
        }
    }
    return resultado;
}
int encerrar_processo(long codigo_saida) {
    int indice;
    liberar_tabelas_paginas(obter_base(processo_atual->ldt[1]), obter_limite(0x0f));
    liberar_tabelas_paginas(obter_base(processo_atual->ldt[2]), obter_limite(0x17));
    for (indice = 0; indice < NUM_TAREFAS; indice++) {
        if (tabela_tarefas[indice] && tabela_tarefas[indice]->pai == processo_atual->pid) {
            tabela_tarefas[indice]->pai = 1;
            if (tabela_tarefas[indice]->estado == ESTADO_ZUMBI) {
                sinalizar(SIGCHLD, tabela_tarefas[1], 1);
            }
        }
    }
    for (indice = 0; indice < MAX_ARQUIVOS; indice++) {
        if (processo_atual->descritores_arquivo[indice]) {
            fechar_arquivo(indice);
        }
    }
    liberar_no_i(processo_atual->diretorio_trabalho);
    processo_atual->diretorio_trabalho = NULL;
    liberar_no_i(processo_atual->raiz);
    processo_atual->raiz = NULL;
    liberar_no_i(processo_atual->executavel);
    processo_atual->executavel = NULL;
    if (processo_atual->lider && processo_atual->terminal >= 0) {
        tabela_terminal[processo_atual->terminal].grupo_processo = 0;
    }
    if (ultimo_processo_coprocessador == processo_atual) {
        ultimo_processo_coprocessador = NULL;
    }
    if (processo_atual->lider) {
        terminar_sessao();
    }
    processo_atual->estado = ESTADO_ZUMBI;
    processo_atual->codigo_saida = codigo_saida;
    notificar_pai(processo_atual->pai);
    reagendar();
    return (-1);
}
int esperar_processo(int pid_filho, unsigned long *endereco_status, int opcoes) {
    int achou, codigo_saida;
    struct tarefa_struct **iterador;
    verificar_area(endereco_status, 4);
repetir:
    achou = 0;
    for (iterador = &ULTIMA_TAREFA; iterador > &PRIMEIRA_TAREFA; --iterador) {
        if (!*iterador || *iterador == processo_atual) {
            continue;
        }
        if ((*iterador)->pai != processo_atual->pid) {
            continue;
        }
        if (pid_filho > 0) {
            if ((*iterador)->pid != pid_filho) {
                continue;
            }
        } else if (!pid_filho) {
            if ((*iterador)->grupo_processo != processo_atual->grupo_processo) {
                continue;
            }
        } else if (pid_filho != -1) {
            if ((*iterador)->grupo_processo != -pid_filho) {
                continue;
            }
        }
        switch ((*iterador)->estado) {
            case ESTADO_PARADO:
                if (!(opcoes & WUNTRACED)) {
                    continue;
                }
                escrever_no_usuario(0x7f, endereco_status);
                return (*iterador)->pid;
            case ESTADO_ZUMBI:
                processo_atual->tempo_usuario_filho += (*iterador)->tempo_usuario;
                processo_atual->tempo_sistema_filho += (*iterador)->tempo_sistema;
                achou = (*iterador)->pid;
                codigo_saida = (*iterador)->codigo_saida;
                liberar_tarefa(*iterador);
                escrever_no_usuario(codigo_saida, endereco_status);
                return achou;
            default:
                achou = 1;
                continue;
        }
    }
    if (achou) {
        if (opcoes & WNOHANG) {
            return 0;
        }
        processo_atual->estado = ESTADO_INTERRUPTIVEL;
        reagendar();
        if (!(processo_atual->sinal &= ~(1 << (SIGCHLD - 1)))) {
            goto repetir;
        } else {
            return -EINTR;
        }
    }
    return -ECHILD;
}

Publicado em 7-7 23:30