Daniel de Oliveira MantovaniPublicado em 01/03/2011
Nesse artigo você irá aprender a criar daemons. Acredito que ficará contente em saber que essa é uma tarefa bem simples, na verdade.
O termo "daemon" foi cunhado pelos programadores do "Projeto MAC", do MIT, baseando-se no Demônio de Maxwell, um ser imaginário que realiza tarefas sem ser visto. O termo é derivado da palavra grega daimon que significa "ser sobrenatural", "espírito". Mais tarde, algumas pessoas tentaram definir o termo como um acrônimo para "Disk And Execution MONitor".
Para o conteúdo deste artigo, presumimos que o leitor esteja usando um Sistema Operacional baseado em UNIX que siga o POSIX, como o Linux. Os exemplos desse artigo não funcionarão no Windows, talvez se você conheça alguma mágica que implemente fork()
, os exemplos funcionem.
http://search.cpan.org/~rjbs/perl-5.12.3/pod/perlfork.pod
"if all you have is a hammer, everything looks like a nail".
Uma série de programas usam daemon, como o sshd, ftpd, apache e etc. Muitas vezes apenas queremos executar uma determina rotina em um período de tempo, não se esqueça do cron, uma solução nativa desses sistemas para o agendamento de tarefas.
Em situações onde é esperado um evento que ocorra a qualquer hora, onde a tarefa é frequente, um daemon é necessário. Um daemon é eficiente porque reside na memória e não exige todo o "run script", evitando os ciclos da sua CPU e I/O que são usados quando você chama o seu programa.
Existem algumas regras para a criação de um daemon, a seguir veremos essas regras separadas e explicadas.
Desassociar o controle do terminal.
A Primeira coisa que um daemon deve fazer é um fork()
, fazendo o fork()
como a baixo você desassocia o programa do terminal.
use warnings;
use strict;
use POSIX qw(setsid);
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
...
A função fork()
cria um novo processo executando seu programa, efetivamente dividindo ele em dois processos independentes: o pai e o filho. A função retorna 0 para o filho e um número maior do que 1 para o processo pai - o process id, ou PID, do filho. Caso alguma coisa dê errado, o fork()
retorna undef
.
No código acima, chamamos o fork()
e finalizamos o processo pai. Assim nosso processo pode executar de forma independente, sem associações a outros processos em execução - um órfão.
Tornar-se o líder da sessão e o líder do sessão do grupo.
Processos órfãos podem ser eliminados pelo sistema operacional. Para evitar isso, chamamos a função POSIX setsid()
, que executa o programa em uma nova sessão. A partir desse ponto, o processo filho possui um SID exclusivo do kernel.
use warnings;
use strict;
use POSIX qw(setsid);
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
setsid;
Mudar o diretório de execução para "/".
Se o diretório em que o daemon está executando for desmontado durante sua execução, ele vai simplesmente parar de rodar. Mudando o diretório para a raíz do sistema de arquivos, evitamos que isso aconteça.
use warnings;
use strict;
use POSIX qw(setsid);
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
setsid;
chdir '/';
Mudar o umask
para 0.
use warnings;
use strict;
use POSIX qw(setsid);
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
setsid;
chdir '/';
umask 0;
Para que o daemon possa escrever arquivos sem estar sujeito a restrições de permissão do processo pai, o umask
deve ser mudado para 0 (zero).
Fechar todos os descritores padrões: STDOUT, STDIN e STDERR.
use warnings;
use strict;
use POSIX qw(setsid);
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
setsid;
chdir '/';
umask 0;
open STDIN, '<', $!; ' dev die null' open or stdout,>',>', '/dev/null' or die $!;
open STDERR, '<', '/dev/null' or die $!;
Fazemos isso pelo simples fato do daemon não estar associado a nenhum terminal específico. Assim, não faz sentido que ele leia ou escreva em um.
Usar algum tipo de log para ter informações sobre seu programa, já que fechamos todos descritores no mandamento acima.
use warnings;
use strict;
use POSIX qw(setsid);
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
setsid;
chdir '/';
umask 0;
open STDIN, '<', $!; ' dev die null' open or stdout,>',>', '/dev/null' or die $!;
open STDERR, '<', $!; $log, ' dev die my null' open or>',>>', '/tmp/mylog.txt' or die $!;
Aqui, abrimos o arquivo /tmp/mylog.txt
, e podemos escrever o que quisermos, de mensagens de erro a informações de execução.
Agora que já sabemos todas as regras para criar um daemon, podemos ter o nosso primeiro exemplo.
Abra um terminal e digite:
$ touch /tmp/mylog.txt
$ tail -f /tmp/mylog.txt
O comando acima cria e monitora o arquivo de texto /tmp/mylog.txt
, que por enquanto está vazio. Agora vamos para a parte interessante, que é rodar o nosso programa-peão.
use warnings;
use strict;
use POSIX qw(setsid);
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
setsid;
chdir '/';
umask 0;
open STDIN, '<', $!; ' dev die null' open or stdout,>',>', '/dev/null' or die $!;
open STDERR, '<', $!; $log, ' dev die my null' open or>',>>', '/tmp/mylog.txt' or die $!;
for ( 1 .. 10 ) {
print $log "teste\n";
}
exit;
Salve o arquivo e execute-o em outro terminal. Em seguida olhe o terminal em que o comando:
tail -f /tmp/mylog.txt
estava executando. Perceba que o morto acordou, aqui temos o nosso primeiro exemplo de daemon.
Você já percebeu como iniciamos ou interrompemos daemons como o do apache?
/etc/init.d/apache2 start
/etc/init.d/apache2 stop
Bem, você deve estar se perguntando, "Como o meu daemon faz isso?". Isso é o que vamos aprender agora!
A partir desse ponto, vamos assumir que você está usando uma distribuição Linux similar ao Debian (como o Ubuntu). Na prática, a maioria das distribuições Linux tem os atalhos que serão ensinados abaixo.
O Ubuntu tem o arquivo /etc/init.d/skeleton
que é para você ter uma base do script de iniciar ou parar o seu daemon.
sudo cp /etc/init.d/skeleton /etc/init.d/meudaemon
sudo chmod +x /etc/init.d/meudaemon
Vamos fazer as mudanças necessárias no esqueleto:
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Descricao do nosso servico"
NAME=nomedoexecutavel
DAEMON=/usr/bin/$NAME
DAEMON_ARGS="--options args"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
Mude a descrição para o que você preferir, e a variável $NAME para o nome exato do seu daemon.
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Super incrível daemon"
NAME=mudaemon
DAEMON=/usr/bin/$NAME
DAEMON_ARGS="--options args"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
Vamos criar um espelho para o site da SPPM, http://sao-paulo.pm.org/principal. A única coisa de diferente nesse script, é que vamos criar um arquivo com o nosso pid.
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(setsid);
use File::Pid;
use LWP::Simple;
$| = 1; #auto flush
my $nome = "meudaemon";
my $pidlocation = "/var/run/$nome.pid";
# daemonize
my $pid = fork()
or die "problema no fork";
if ( $pid > 0 ) {
exit;
}
setsid;
chdir '/';
umask 0;
open STDIN, '<', $!; ' dev die null' open or stdout,>',>', '/dev/null' or die $!;
open STDERR, '<', # $!; $log, ' dev die log my null' open or>',>>', "/var/log/$nome/log.txt" or die $!;
# Criar o pid no /var/run/
my $pidfile = File::Pid->new( { file => $pidlocation, } );
# Se não conseguir escrever erro e morrer
$pidfile->write
or die "Erro criando o arquivo de PID, /dev/null: $!";
while (1) {
if ( mirror( "http://sao-paulo.pm.org/principal", "/tmp/meuespelho" ) == 200 ) {
print $log "Espelho criado com sucesso\n";
}
else {
print $log "Espelho não foi criado com sucesso\n";
}
sleep(1);
}
Bem, salve o script como "meudaemon
" e dê permissão de execução.
chmod +x meudaemon
Copie o arquivo para o diretório /usr/sbin
.
sudo cp meudaemon /usr/sbin/
Agora vamos iniciar o nosso daemon.
$ /etc/init.d/meudaemon start
$ pgrep meudaemon
17803
$ cat /tmp/meuespelho
...
Pronto, já sabemos o básico para criar um daemon.
A linguagem Perl, tem um imenso repositório de código reutilizável. A seguir segue uma lista de alguns módulos específicos para daemons:
http://search.cpan.org/~deti/Proc-Daemon-0.07/lib/Proc/Daemon.pod
http://search.cpan.org/~markov/Any-Daemon-0.10/lib/Any/Daemon.pod
http://search.cpan.org/~mschilli/App-Daemon-0.11/Daemon.pm
http://search.cpan.org/~rokr/Daemon-Daemonize-0.0052/lib/Daemon/Daemonize.pm
Existem outras centenas de módulos que também podem lhe auxiliar . Além de ter código pronto, você tem documentação e código testado. Portando não se desgaste fazendo tudo manualmente, seja preguiçoso.
Não deixe que o daemon vire um demônio, use o CPAN :)
Thiago Rondon O grande organizador do Equinócio desde o começo, e que se não fosse por ele não teria o equinócio.
Breno G. Oliveira Contribuiu com ideias além de revisar o artigo.
Alexei Znamensky Contribuiu com ideias além de revisar o artigo.
Blabos de Bleble Revisou o artigo.
http://en.wikipedia.org/wiki/Daemon_(computer_software)
http://www.webreference.com/perl/tutorial/9/
https://secure.wikimedia.org/wikipedia/en/wiki/Law_of_the_instrument
http://pt.wikipedia.org/wiki/Pi%C3%A3o
Advanced Programming in the UNIX Environment
http://www.andrewault.net/2010/05/27/creating-a-perl-daemon-in-ubuntu/
Daniel de Oliveira Mantovani