Descrição e Criação de Processos no Linux

Estados dos processos no Linux: - Pronto: quando o processo está esperando para ser executado. - Executando: quando o processo está sendo executado pelo CPU. - Bloqueado: quando o processo está aguardando por um evento ou recurso. Quando um processo chama do_fork(), ele se torna pronto (TASK_RUNNING). - Pronto: quando o escalonador seleciona um processo pronto para execução, ele passa para o estado executando. - Executando: pode retornar ao estado pronto se um processo de maior prioridade to interrompê-lo ou se o tempo de CPU atribuído ao processo acabar. Também pode entrar no estado bloqueado se estiver aguardando algum evento ou recurso. Chamar do_exit() termina o processo. - Bloqueado: quando o evento esperado ocorre ou o recurso se torna disponível, o processo volta ao estado pronto. Em Linux, os estados prontos e executando são representados pelo mesmo valor TASK_RUNNING. Um processo em TASK_RUNNING está apto a ser executado, mas depende de obter o controle do CPU para realmente executar. Um processo que está em execução e chama a função de biblioteca do usuário exit() entra no kernel para executar do_exit() e passa para o estado TASK_ZOMBIE. Processos em estado TASK_ZOMBIE, conhecidos como processos zumbis, são removidos pelo kernel quando os recursos associados a eles são liberados. Existem dois tipos de estados bloqueados: - TASK_INTERRUPTIBLE: pode ser acordado por sinais ou pela função wake_up(). - TASK_UNINTERRUPTIBLE: só pode ser acordado pela função wake_up(). Os estados dos processos no Linux são definidos no arquivo include/linux/sched.h na linha 203. Criação de Processos ==== Processo Inicial (PID 0) ------- O primeiro nó da lista duplamente encadeada é init_task. A estrutura init_task, que descreve o processo inicial, é inicializada com codificação fixa localizada em init/init_task.c na linha 18. O código de inicialização da estrutura init_task é mostrado abaixo: ``` // estrutura de tarefa inicial struct task_struct init_task = INIT_TASK(init_task); EXPORT_SYMBOL(init_task);


O macro `INIT_TASK` está definido em `include/linux/init_task.h` na linha 173. Gestão de Memória -------- Na estrutura `struct task_struct`, os campos `struct mm_struct *mm, *active_mm` gerenciam a memória do processo. O campo `mm` descreve o espaço de endereços do processo, enquanto `active_mm` é usado para gerenciamento de memória ativo. O MMU (Unidade de Gerenciamento de Memória) lida com a conversão entre endereços lógicos e físicos. Em uma arquitetura x86 de 32 bits, cada processo tem 4 GB de espaço de endereços lógico. Relacionamentos entre Processos ------------ A estrutura `struct task_struct` contém ponteiros para o processo pai (`struct task_struct __rcu* real_parent` e `struct task_struct __rcu* parent`) e listas duplamente encadeadas para os processos filhos (`struct list_head children`) e irmãos (`struct list_head sibling`). Estado do Processo na CPU ------------------------- A estrutura `struct thread_struct thread`, definida em `arch/x86/include/asm/processor.h` na linha 468, mantém informações sobre o estado do processo na CPU, como os registradores ESP e EIP. Criação de Processos --------- Durante a inicialização do sistema, dois threads kernel são criados através da função `kernel_thread`: `kernel_init` e `kthreadd`. O primeiro inicia o processo `init` no modo de usuário, enquanto o segundo gerencia todos os outros threads kernel. A criação do processo `init` (PID 1) envolve a cópia do processo inicial (PID 0) e a substituição de alguns campos, como o PID, seguida da carga do programa `init`. Da mesma forma, o processo `kthreadd` (PID 2) também é criado copiando o processo inicial. 1.Criação de Processos em Modo de Usuário ```
int pid = fork();
if(pid < 0) {        // erro no fork
} else if(pid == 0) { // processo filho
} else {}

A função fork() cria um novo processo copiando o processo atual. Ambos os processos executam o mesmo código após o fork. 2.Sistema de Chamadas fork()Ao chamar fork(), uma interrupção é gerada com a instrução int $0x80, que salva o contexto do processo atual na pilha do kernel e transfere o controle para o kernel. O kernel então executa as rotinas necessárias para lidar com a chamada de sistema, como a cópia do espaço de endereços do processo pai para o processo filho. 3.Processo Interno de fork()O processo de criação de um novo processo envolve a função do_fork(), que é chamada internamente por fork(), vfork(), clone() e kernel_thread(). A função do_fork() é responsável por configurar a nova estrutura de tarefa do processo filho e adicioná-lo à fila de escalonamento. ``` long do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user* parent_tidptr, int __user* child_tidptr);


Os parâmetros incluem: - `clone_flags`: sinalizadores que controlam quais recursos do processo pai devem ser copiados. - `stack_start`: endereço inicial da pilha do processo filho. - `stack_size`: tamanho da pilha do processo filho. - `parent_tidptr` e `child_tidptr`: endereços onde o PID do processo pai e filho serão armazenados. Dentro de `do_fork()`, a função `copy_process()` é chamada para duplicar o processo pai e ajustar os campos necessários, como o PID. Depois disso, a função `wake_up_new_task()` é usada para adicionar o processo filho à fila de escalonamento. A função `copy_process()` realiza várias tarefas, incluindo a verificação de segurança, a duplicação da estrutura de tarefa, a configuração de novos recursos e a inicialização de estruturas específicas do processo.</div>

Tags: linux-kernel process-management task_struct fork-system-call

Publicado em 8-14 12:22