Sessões de processos
Uma funcionalidade bastante interessante, no entanto, desconhecida por muitos dos desenvolvedores e administradores de sistemas rodando Linux, são os grupos e sessões de processos. Talvez isso se dê pelo fato de esta funcionalidade ser praticamente transparente, até que nos deparamos com uma situação inoportuna, onde seu conhecimento e uso se torna indispensável.
Para que possamos exemplificar o uso do conceito, imagine uma aplicação qualquer que necessite criar um novo processo, e este por sua vez, crie diversos outros subprocessos. Se tal aplicação decidisse, em determinado momento, que tal processo deve ser cancelado ou parado temporariamente, seria necessário enviar um sinal ao processo. Entretanto, se fizéssemos isso sem tomar as devidas precauções, os subprocessos ativos deste não receberiam o sinal e ficariam rodando inadvertidamente. O tratamento de processos como um conjunto, evitando situações como essa, é uma das situações onde o uso de grupos de processos se aplica perfeitamente.
Um grupo de processos é uma coleção de processos que possuem um identificador de grupo (group id) em comum. Esse identificador é fornecido pelo processo chamado "líder do grupo". Embora esse termo possa gerar a impressão equivocada de que é difícil para um processo tornar-se líder de um grupo, qualquer processo pode facilmente fazer isso, bastando para que ele "queira", chamando a função apropriada. Tal ação gerará um novo identificador de grupo (convencionalmente, o identificador de processo, ou pid, do líder do grupo, é utilizado) e o tornará líder de seu próprio grupo.
Agora que entendemos o conceito de grupo de processos, torna-se trivial explicar o de sessão de processos. Uma sessão de processos trata-se de um conjunto de grupos de processo que compartilham um mesmo identificador de sessão (session id), pertencente originalmente ao líder a sessão. Assim como no caso de grupos, qualquer processo pode se tornar um líder da sessão, se assim o desejar, e chamar a função adequada. Isso gerará um novo identificador de sessão (convencionalmente, também é utilizado o pid do líder da sessão) e o tornará líder de sua própria sessão. Além disso, ao se tornar líder da sessão, o processo também se tornará líder de um novo grupo de processos.
É também interessante que entendamos como um processo se torna parte de um grupo e sessão existentes. Sempre que um novo processo é criado, ele herda de seu criador o identificador de grupo e o identificador de sessão. Caso seja oportuno, um processo pode também solicitar a troca de grupo, bastando para isso que exista, no grupo de destino, um processo que pertença à mesma sessão do processo solicitante.
Como curiosidade e forma de entendimento da idéia, uma aplicação shell (bash, ash, etc) é, normalmente, líder de uma sessão, e cada programa (processo) que se executa na linha de comando se torna um líder de seu grupo (podemos utilizar o comando "setsid", para que o processo também se torne líder de sua própria sessão). Por isso, quando pressionamos CTRL-C no terminal, o processo ativo e todos seus subprocessos recebem o sinal SIGINT (gerado pelo CTRL-C), e normalmente terminam. O identificador de grupo e identificador de sessão de todos os processos do sistema podem ser verificados com o comando ps -ajx.
Aprofundando de forma um pouco mais técnica o nosso conhecimento, vejamos algumas das chamadas de sistema para lidar com grupos de processos.
setpgrp()
Faz do processo um líder de grupo. Imediatamente após a função, tal grupo contém somente o próprio processo.
setpgid(process_id, group_id)
Se chamada com os parâmetros (0,0), obtém-se o mesmo resultado da função setpgrp(). Entretanto, através desta também é possível trocar o grupo de um processo, conforme restrições vistas acima.
setsid()
Torna o processo um líder de grupo e um líder de sessão.
killpg(group_id, signal_id)
Envia um sinal para um grupo de processos. Para saber mais sobre essas funções, consulte a man page delas com o comando man 2 <nome_da_função>.
Agora que dominamos a teoria sobre grupos e sessões de processos, torna-se fácil entendermos como proceder para que, em nosso exemplo, o processo criado e todos seus subprocessos sejam cancelados ou parados temporariamente. Bastaria que, ao criarmos o novo processo, o tornássemos parte de seu próprio grupo e, ao enviarmos o sinal para interrupção, o fizéssemos utilizando a função killpg(), de forma que todos os processos pertencentes ao grupo criado recebessem o sinal.
Gustavo Niemeyer - niemeyer@conectiva.com.br