UNIVERSIDADE ESTADUAL DE PONTA GROSSA SETOR DE CIÊNCIAS AGRÁRIAS E DE TECNOLOGIA DEPARTAMENTO DE INFORMÁTICA JUAN CASSIUS CARNEIRO PEREIRA PEDRO M. M. M. NETO FUNÇÕES E MECANISMOS DE MANIPULAÇÃO DE PROCESSOS EM LINUX PONTA GROSSA 2016 JUAN CASSIUS CARNEIRO PEREIRA PEDRO M. M. M. NETO FUNÇÕES E MECANISMOS DE MANIPULAÇÃO DE PROCESSOS EM LINUX Trabalho apresentado para obtenção de nota parcial na disciplina de Sistemas Operacionais. Prof. Dr. Dierone Foltran. PONTA GROSSA 2016 SUMÁRIO INTRODUÇÃO................................................................................................ 4 REVISÃO BIBLIOGRÁFICA ........................................................................... 5 1.1. VISUALIZANDO UM PROCESSO .................................................... 6 1.1.1. Identificador do processo ............................................................ 6 1.1.2. Visualizando os processos ativos no Linux ................................. 6 1.1.3. Estado de execução de um processo ......................................... 6 1.2. CRIANDO UM PROCESSO .............................................................. 8 1.2.1. Utilizando a chama system.......................................................... 8 1.2.2. Utilizando a função fork ............................................................... 8 1.2.3. Utilizando a função exec ............................................................. 8 1.3 ESCALONAMENTO DE PROCESSOS EM LINUX ............................ 10 1.4 SINAIS ................................................................................................ 11 1.5 FINALIZANDO UM PROCESSO NO LINUX ....................................... 12 1.5.1 Utilizando a chamada de sistema wait ......................................... 12 CONCLUSÃO ............................................................................................... 13 INTRODUÇÃO Com a evolução e o desenvolvimento de novos sistemas operacionais e o surgimento da multiprogramação, foi essencial a fundamentação do conceito de processo pois é a base dos sistemas multiprogramados. Os sistemas operacionais continuaram se desenvolvendo e acabaram se dividindo em vários tipos. O Linux, que é um sistema multiprogramado, acabou derivando da família BSD do UNIX. Este trabalho tem como base a revisão bibliográfica de funções e manipulação de processos nos sistemas operacionais do tipo Linux. REVISÃO BIBLIOGRÁFICA Segundo Oliveira; Carissim e Toscani (2004), o conceito de processo é fundamental para qualquer sistema operacional multiprogramado, ele é definido como uma instância de um programa em execução. Processos podem representar tanto programa do usuário quanto tarefas do sistema operacional. No Linux, quando o usuário visualiza um terminal aberto, este é um processo em execução. O terminal pode estar rodando um shell, que é um novo processo. Quando utilizada uma função do shell, o programa correspondente é executado em um novo processo. A maioria das funções de manipulação de processos que serão vistas neste trabalho, são similares as usadas nos sistemas UNIX. A maioria é declarada no arquivo header <unistd.h>. 1.1. VISUALIZANDO UM PROCESSO O sistema operacional sempre vai ter vários processos rodando, independente de um usuário estar utilizando o computador ou não. Cada programa que está sendo executado pode ter um ou mais processos. 1.1.1. Identificador do processo Cada processo no Linux é identificado por um process ID único. Não existem duas process IDs do processo com o mesmo valor, algumas vezes é referida como pid. As process IDs dos processos são números de 16-bits que são atribuídos sequencialmente pelo próprio Linux quando o processo é criado e é apagado quando o processo é encerrado. Ao executar o mesmo programa após fechá-lo, o programa terá um pid diferente do anterior. Cada processo tem um processo pai, com exceção do init. Os processos no Linux são organizados como uma árvore, sendo o init como a raiz dela. Cada processo tem um identificador do processo pai, ou parent process ID, também conhecido como ppid. Quando for identificar um process ID em um programa em C ou C++, utilizase o tipo de dado pid_t, que está definido na biblioteca <sys/types.h>. Qualquer programa pode obter o próprio process ID ou do pai que está sendo executado utilizando as funções getpid e getppid respectivamente. 1.1.2. Visualizando os processos ativos no Linux No Linux, para verificar os processos ativos no sistema, utiliza-se o comando ps ou top no terminal. O comando ps retorna ao usuário uma foto dos processos que estavam sendo executados no momento que o comando foi utilizado. Para mostrar todos os processos do sistema usando a sintaxe padrão, utiliza-se o comando ps -e. Para mostrar quais informações o ps deve retornar, utiliza-se ps -o, podendo mostrar o pid e o ppid de cada processo utilizando ps -o pid,ppid. O comando top retorna ao usuário uma lista que se mantém atualizada de todos os processos que estão sendo executados no Linux. 1.1.3. Estado de execução de um processo Segundo de Oliveira; Carissim e Toscani (2004), durante sua fase de execução, todo processo em Linux passa por diferentes estados. O estado de task_running é quando o processo está em execução ou esperando para ser executado. O task_interruptible é o estado em que o processo está bloqueado, esperando que determinada condição esteja satisfeita, como operação de entrada e saída ou a interrupção de um software emitida por outro processo. Retorna ao task_running quando satisfeito. O task_uninterruptible é o estado que representa uma condição crítica e não pode ser alterado até a conclusão. Normalmente ocorre com um evento relacionado ao hardware. O task_stoppped é o estado em que o processo recebe um sinal e só retorna sua execução com outro sinal. O task_zombie é o estado que um processo filho assume ao terminar sua execução, enquanto o processo pai não utiliza uma chamada de sistema para o retorno de sua execução. 1.2. CRIANDO UM PROCESSO Existem duas maneiras de se criar um processo no Linux, usando as funções fork e exec ou utilizando o system. Ambas são chamadas do sistema, ou seja, chamam o sistema operacional para fazer algo que o usuário não pode. 1.2.1. Utilizando a chama system Uma das maneiras de se criar um processo é usando a chama de sistema system. Essa função não é tão eficiente ou segura quanto as funções fork e exec, pois ela funciona como se o usuário digitasse o comando em um shell, o Bourne shell padrão (bin/sh), portanto ela está vulnerável a todas as falhas de um shell. A função passa o comando para o shell executar, caso a função execute comandos de root, ela terá diferentes resultados em diferentes distribuições do Linux. Em vários sistemas UNIX, o caminho (bin/sh) é apenas uma conexão simbólica com outro shell. No Linux, o shell usado é o bash(Bourne-Again Shell), mas cada distribuição dele usa determinada versão do bash. 1.2.2. Utilizando a função fork O fork é uma chama do sistema que cria uma cópia exata do programa que a chamou, com as mesmas variáveis, registros. O programa que chamou a função fork será o processo pai e o novo programa gerado através da função será o processo filho. A partir do momento em que a função fork é chamada e os dois processos são criados, cada processo segue um rumo diferente do mesmo lugar. Para chamar a função, não se passa nenhum argumento, o sistema operacional trabalha com o resto e retorna um pid, caso ocorra um erro, a função retorna um número negativo. O pid retornado é um número do tipo pid_t e tem um comportamento especial. Dentro do processo filho, o pid tem valor 0. Dentro do processo pai, o pid tem o valor do processo filho. 1.2.3. Utilizando a função exec Exec é um grupo de funções que acabam fazendo o programa filho executar como outro programa, substituindo a execução do programa anterior. Para se utilizar a função exec necessitam só da biblioteca <unistd.h>. Existem vários tipos de funções dentro da família do exec, com diferentes capacidades. Funções que contém a letra ‘p’ nos nomes aceitam o nome do programa e procura ele no diretório de execução atual. As que não contém, devem ter o caminho completo até o programa executado. As funções que contém a letra ‘v’ nos nomes, aceitam um vetor de argumentos de string terminados em nulo. Já as terminadas em ‘l’, acabam recebem argumento por argumento, sendo a principal diferença. Funções que contém a letra ‘e’ nos nomes, aceitam um argumento adicional, um vetor de strings que contém variáveis do ambiente, mas ainda deve ser um vetor terminado em nulo. O primeiro parâmetro da função, é o caminho que o arquivo deve ser executado e a função só vai retornar caso ocorra um erro. Como o código que chama a função acaba sendo destruído, não há sentido em chamá-la sem o uso da função fork antes. 1.3 ESCALONAMENTO DE PROCESSOS EM LINUX Em Linux, o escalonamento de processos pais e filhos são feitos de maneira independente, fazendo com que não se saiba qual ira ser executado primeiro nem sua duração até que o Linux interrompa o processo. Existe uma maneira de priorizar processos em Linux, pode-se atribuir um nível de importância usando o comando nice. O valor inicial de todo processo é 0, esse valor pode ser alterado sendo que quanto maior, menor a prioridade que o sistema dará ao processo, podendo também esse valor ser negativo. Uma aplicação desse comando é quando se necessita executar um processo muito pesado e não desejase sobrecarregar o sistema. Um processo pode sofrer reajuste de prioridade enquanto está sendo executado, isso pode ser feito com o comando renice. 1.4 SINAIS Segundo Mitchell; Samuel e Oldham (2001), sinais são um tipo de comunicação assíncrona entre processos concorrentes. Assim que um processo recebe um sinal ele o processa imediatamente. Um processo pode executar uma de várias alternativas quando recebe um sinal. Ele pode seguir a rotina padrão, usar tratamento de sinal ou simplesmente ignorá-lo. Os sinais são identificados simbolicamente pelo prefixo SIG. Um uso bem comum é o dos sinais SIGKILL e SIGTERM, os dois têm a mesma função, encerrar o processo que o recebeu, somente diferem quanto ao tratamento de sinais, que só pode ser feito pelo processo que recebe o sinal SIGTERM. Para tratar um sinal que chega ao processo usa-se a função signal() com dois parâmetros, o primeiro é o nome do sinal, o segundo é o nome do tratador criado dentro do programa que gerará o processo. 1.5 FINALIZANDO UM PROCESSO NO LINUX Para finalizar um processo no Linux, basta usar o comando kill no terminal, informando o sinal e o pid do processo. O comando kill é usando para mandar um sinal a um processo. Se usado sem nenhum sinal, ele irá mandar finalizar um processo. É possível ver a lista de sinais usando o comando kill -l. O comando killall é usado para matar todos os processos possíveis ou com determinados nomes. 1.5.1 Utilizando a chamada de sistema wait Como falado no tópico 1.1.3, alguns processos podem requerir a resposta do final do processo filho, para isso utiliza-se a chamada wait. Ela trava o processo que fez o requerimento até que um processo filho termine. Se necessário a finalização de um filho específico, pode-se utilizar a chamada waitpid. Existem também as chamadas de sistemas wait3 e wait4 que oferecem opções a mais sobre o processo que irá morrer. CONCLUSÃO Processos são a base dos sistemas operacionais multiprogramados. Uma boa fundamentação sobre seus conceitos e habilidade de manipulá-los são essenciais para compreensão do funcionamento dos sistemas e contribuem para o aprimoramento do usuário como desenvolvedor. REFERÊNCIAS MITCHELL, M.;SAMUEL, A.; OLDHAM, J. Advanced Linux Programming. 2001. de OLIVEIRA, R. S.; CARISSIM, A. da S.; TOSCANI, S. S. Sistemas Operacionais. 2004.