php e seus demônios
DESCRIPTION
Criando Daemons em PHP Macro tópicos • Forks • Sinais • Daemons • Spawn • Zombies • IPCTRANSCRIPT
PHP e seus demôniosCRIANDO DAEMONS EM PHP
PALESTRANTE
�2
Henrique Moody
• Desenvolvedor web com foco em PHP desde 2007
• Usuário assíduo de Linux desde 2008 • Zend Certified Engineer 5.3 desde 2011 • Contribui com vários projetos Open Source • Líder Técnico • Desenvolvedor PHP Senior
�3
PALESTRA
�4
PHP e seus demônios
• Forks • Sinais • Daemons • Spawn • Zombies • IPC
�5
FORKS
�6
• Em sistemas operacionais Unix-‐like fork é uma operação em que um processo cria uma cópia de si mesmo
• Fork é uma forma de um processo executar outro ou outros processos a partir de si mesmo
• Quando a cópia do processo é criada essa cópia é chamada de processo filho tornando o processo original o processo pai
• No PHP é necessária a extensão PCNTL habilitada e extremamente recomendável a extensão POSIX também habilitada
�7
PCNTL (Process Control)
• Deve ser habilitada no momento da compilação do PHP (——enable-pcntl)
• Suportada apenas para sistemas Unix-‐like • Não funciona em web servers • Cria e gerencia processos e sinais
�8
POSIX (Portable Operating System Interface)
• Habilitada por padrão na compilação do PHP • Suportada apenas para sistemas Unix-‐like • Fornece uma API padrão para desenvolvimento em sistemas Unix-‐like
• Gerencia processos, sessões, grupos, usuários e arquivos de sistemas Unix-‐like
�9
Funcionamento
• A função pcntl_fork() criará o fork e retornará um valor diferente para cada processo (pai e filho)
• Caso pcntl_fork() retorne -1 ocorreu um erro no fork • Caso pcntl_fork() retorne 0 houve sucesso no fork. O processo atual é o filho
• Caso pcntl_fork() retorne um número maior do que 0 houve sucesso no fork. O processo atual é o pai e o retorno de pcntl_fork() é o PID do filho
• Nada impede um processo filho criar forks de si mesmo • Todas as variáveis inicializadas no processo pai estarão disponíveis para os filhos
�10
Forks/pcntl.php<?php$pid = pcntl_fork();if ($pid == -1) { // Falha na criação do fork echo 'Falha na criação do fork' . PHP_EOL;!} elseif ($pid > 0) { // Sou o processo pai echo 'Fork criado com sucesso sob o PID ' . $pid . PHP_EOL;!} else { // Sou o processo filho, em background mail('vagrant@localhost', 'Lorem ipsum', 'Dolor sit amet');}
�11
Forks/pcntl+posix.php<?php$pid = pcntl_fork();if ($pid == -1) { // Falha na criação do fork echo 'Falha na criação do fork' . PHP_EOL;!} elseif ($pid > 0) { // Sou o processo pai echo 'Fork criado com sucesso sob o PID ' . $pid . PHP_EOL;!} else { // Sou o processo filho, em background if (0 !== posix_getuid()) { error_log('É necessário ser root para alterar informações do processo'); exit(2); }! if (! posix_setuid(1000)) { error_log('Não foi possível definir o usuário do processo como 1000'); exit(3); }! if (! posix_setgid(1000)) { error_log('Não foi possível definir o grupo do processo como 1000'); exit(4); }! mail('vagrant@localhost', 'Lorem ipsum', 'Dolor sit amet');}
�12
SINAIS
�13
• Em sistemas Unix-‐like, um sinal é uma notificação de software a um processo da ocorrência de um evento
• Um sinal é gerado pelo SO quando o evento que causa o sinal acontece
• Existem vários sinais que podem ser enviados para um processo, alguns deles podem ser manipulados pela aplicação já outros apenas pelo próprio SO
• Podemos enviar através da função posix_kill() • Podemos definir um callback para manipular sinais através da função pcntl_signal()
�14
Lista de constantes de sinais do PHP• SIGABRT• SIGALRM• SIGBABY• SIGBUS• SIGCHLD• SIGCONT• SIGFPE• SIGHUP• SIGILL• SIGINT• SIGIO
• SIGIOT• SIGKILL• SIGPIPE• SIGPROF• SIGQUIT• SIGSEGV• SIGSTOP• SIGSYS• SIGTERM• SIGTRAP• SIGTSTP
• SIGTTIN• SIGTTOU• SIGURG• SIGUSR1• SIGUSR2• SIGVTALRM• SIGWINCH• SIGXCPU• SIGXFSZ
�15
Mais comuns• SIGHUP: enviado quando o sessão (terminal) do processo é fechada
• Pode ser interceptado • SIGINT: enviado quando um se pretende interromper o processo
• Pode ser interceptado • Pode ser enviado via teclado, com Control-C, e em alguns sistemas com delete ou break
• SIGTSTP: enviado quando se pretende pausar o processo • Pode ser interceptado • Pode ser enviado via teclado, com Control-Z
• SIGCONT: enviado quando se pretende despausar o processo após SIGTSTP • Pode ser interceptado
• SIGTERM: enviado quando se pretende terminar o processo (amigavelmente). • Pode ser interceptado
• SIGQUIT: enviado quando se pretende encerrar o processo e obter um dump de memória. • Pode ser interceptado • Pode ser enviado via teclado, com Control-\
• SIGKILL: enviado quando se pretende encerrar imediatamente o processo • Não ser interceptado
�16
Sinais/envio.php<?php!// Envia um 0 (verifica se o PID é válido ou não)posix_kill($pid, 0);!// Envia um SIGUSR1 (User-defined signal 1)posix_kill($pid, SIGUSR1);!// Envia um SIGSTOP (pausa a execução do processo)posix_kill($pid, SIGSTOP);!// Envia um SIGCONT (continua a execução do processo)posix_kill($pid, SIGCONT);!// Envia um SIGKILL (mata instantâneamente o processo)posix_kill($pid, SIGKILL);
�17
Sinais/manipulacao.php<?php!declare(ticks = 1);function signalHandler($signal){ switch ($signal) { case SIGQUIT; error_log('Me fecharam com o teclado (Control-\)'); exit(1); case SIGINT: error_log('Me interromperam com o teclado (Control-C)'); exit(1); case SIGHUP: error_log('Fecharam meu terminal'); exit(1); case SIGTERM: error_log('Me pediram para me matar'); exit(0); }} !pcntl_signal(SIGQUIT, 'signalHandler');pcntl_signal(SIGINT, 'signalHandler');pcntl_signal(SIGHUP, 'signalHandler');pcntl_signal(SIGTERM, 'signalHandler');pcntl_signal(SIGTSTP, 'signalHandler');pcntl_signal(SIGTSTP, SIG_IGN); // SIG_IGN faz com que SIGTSTP seja ignoradopcntl_signal(SIGCONT, SIG_IGN); // SIG_IGN faz com que SIGCONT seja ignorado!echo 'PID: ' . getmypid() . PHP_EOL;while (true) { echo date('Y-m-d H:i:s') . PHP_EOL; sleep(1);}
�18
DAEMONS
�19
• Acrônimo de Disk And Execution MONitor (Monitor de Execução e de Disco)
• Em Unix e outros sistemas operacionais multi-‐tarefas é um programa de computador que roda de forma independente em background, ao invés de ser controlado diretamente por um usuário
• Em um ambiente Unix, o processo pai de um daemon é normalmente (mas nem sempre) o processo init (PID=1)
• Alguns exemplos de daemons são: MySQL Server, Apache Server, Nginx Server, Cron
• Muitas vezes, um programa se torna um daemon através de forking
�20
�21
CRIANDO UM DAEMON
Passo a passo
1. Fork off and die 2. Máscara de criação dos arquivos 3. Entradas e saídas 4. Logging 5. Desligar sessão (SID) 6. Working directory 7. Locking
�22
Fork off and die
• Você apenas criará o fork e encerrará imediatamente o processo pai
• O processo filho será o daemon, executando em background
�23
Daemons/fork.php<?php$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL; exit(2);!} elseif ($pid > 0) { echo 'Daemon inicializado (PID: ' . $pid . ').' . PHP_EOL; exit();}!while (true) { mail('vagrant@localhost', 'Lorem ipsum', 'Dolor sit amet'); sleep(2);}
�24
Máscara de criação dos arquivos
• Para garantir que você possa ler e escrever arquivos restaure o umask para o padrão do sistema, com umask(0)
�25
Daemons/fork+umask.php<?php$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL; exit(2);!} elseif ($pid > 0) { echo 'Daemon inicializado (PID: ' . $pid . ').' . PHP_EOL; exit();}!umask(0);!while (true) { mail('vagrant@localhost', 'Lorem ipsum', 'Dolor sit amet'); sleep(2);}
�26
Entradas e saídas
• O daemon não possui interação com o usuário, portanto você não deve permitir que os métodos de entrada e saída (STDIN, STDOUT e STDERR) sejam utilizados
• Você pode fechar STDIN, STDOUT e STDERR, mas caso você esteja utilizando essas constantes com certeza você terá problemas
• Você também pode utilizar as funções ob_* para evitar outputs
�27
Daemons/fork+umask+file_descriptors.php<?php$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL; exit(2);!} elseif ($pid > 0) { echo 'Daemon inicializado (PID: ' . $pid . ').' . PHP_EOL; exit();}!umask(0);!fclose(STDIN);fclose(STDOUT);fclose(STDERR);!$fd0 = fopen('/dev/null', 'r');$fd1 = fopen('/tmp/psd.log', 'a');$fd2 = fopen('php://stdout', 'a');!while (true) { mail('vagrant@localhost', 'Lorem ipsum', 'Dolor sit amet'); sleep(2);}
�28
Logging
• Visto que não interação entre o daemon e o usuário, logs são uma ótima forma de obter feedback de um daemon
• Você pode fazer logs em: ‣ Arquivos ‣ Bancos de dados relacionais ‣ Bancos de dados não-‐relacionais ‣ Message Queue ‣ Syslog ‣ …
�29
Daemons/fork+umask+file_descriptors+logging.php<?php$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL; exit(2);!} elseif ($pid > 0) { echo 'Daemon inicializado (PID: ' . $pid . ').' . PHP_EOL; exit();}!umask(0);!fclose(STDIN);fclose(STDOUT);fclose(STDERR);!$fd0 = fopen('/dev/null', 'r');$fd1 = fopen('/tmp/psd.log', 'a');$fd2 = fopen('php://stdout', 'a');!openlog('PSD', LOG_PID | LOG_CONS, LOG_LOCAL0);while (true) { syslog(LOG_DEBUG, 'Envio de email iniciando'); $sent = mail('vagrant@localhost', 'Lorem ipsum', 'Lorem ipsum dolor sit amet'); if (true === $sent) { syslog(LOG_DEBUG, 'Envio de email terminado sucesso'); continue; } syslog(LOG_ERR, 'Falha ao enviar email'); sleep(2);}closelog();
�30
!
Desligar sessão (SID)
• Mesmo que o processo filho seja executado em background, não dependendo do processo pai, eles estão na mesma sessão
• Quando a sessão terminar (o terminal fechado, por exemplo), o sistema matará o processo filho
• A função posix_setsid() cria uma nova sessão para o processo filho, desvinculando-‐o do processo pai e sua sessão
• O processo filho passa a ter o init (processo inicial que carrega todos os outros processos do sistema) como processo pai
�31
Daemons/fork+umask+file_descriptors+logging+detach_sid.php<?php$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL; exit(2);!} elseif ($pid > 0) { echo 'Daemon inicializado (PID: ' . $pid . ').' . PHP_EOL; exit();}!umask(0);!fclose(STDIN);fclose(STDOUT);fclose(STDERR);!$fd0 = fopen('/dev/null', 'r');$fd1 = fopen('/tmp/psd.log', 'a');$fd2 = fopen('php://stdout', 'a');!openlog('PSD', LOG_PID | LOG_CONS, LOG_LOCAL0);!if (posix_setsid() < 0) { syslog(LOG_ERR, 'Não foi possível desvincular processo de sua sessão'); exit(2);}!while (true) { /** Payload **/ }closelog();
�32
Working directory
• O filho herda o working directory do pai • Este working directory pode ser um volume montado que pode ser desmontado em algum momento
• Para desmontar um volume o sistema irá matar qualquer processo que ainda está usando o diretório
�33
Daemons/fork+umask+file_descriptors+logging+detach_sid+chdir.php<?php$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL; exit(2);!} elseif ($pid > 0) { echo 'Daemon inicializado (PID: ' . $pid . ').' . PHP_EOL; exit();}!umask(0);!fclose(STDIN);fclose(STDOUT);fclose(STDERR);!$fd0 = fopen('/dev/null', 'r');$fd1 = fopen('/tmp/psd.log', 'a');$fd2 = fopen('php://stdout', 'a');!openlog('PSD', LOG_PID | LOG_CONS, LOG_LOCAL0);!if (posix_setsid() < 0) { syslog(LOG_ERR, 'Não foi possível desvincular processo de sua sessão'); exit(2);}!chdir(__DIR__);!while (true) { /** Payload **/ }closelog();
�34
pidfile
• Contém o PID do daemon • Impede que o daemon seja executado mais de uma vez
�35
Daemons/fork+umask+file_descriptors+logging+detach_sid+chdir+pidfile.php<?php!$pidfile = '/var/run/psd/daemon.pid';if (file_exists($pidfile)) { $daemonPid = (int) file_get_contents($pidfile); if (true === posix_kill($daemonPid, 0)) { echo 'Daemon já em execução (PID ' . $daemonPid . ').' . PHP_EOL; exit(2); } unlink($pidfile);}!$pidfileHandler = fopen($pidfile, 'w+');!if (! flock($pidfileHandler, LOCK_EX | LOCK_NB)) { echo 'Falha ao bloquear acesso externo ao pidfile' . PHP_EOL; exit(3);}!$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL; exit(4);!} elseif ($pid > 0) { if (! fwrite($pidfileHandler, $pid)) { echo 'Falha ao escrever PID no pidfile' . PHP_EOL; exit(5); }! echo 'Daemon inicializado (PID: ' . $pid . ').' . PHP_EOL; exit();}!register_shutdown_function('unlink', $pidfile); !// Corpo do daemon
�37
SPAWN
�38
• Muito utilizado no processamento de filas quando você precisa de processos concorrentes
• Um processo pode criar outros processos e delegar tarefas para cada um deles
• Esse recurso muitas vezes é confundido com multi-‐threading, mas não é isso. O PHP não possui suporte a este recurso mas existe uma extensão PECL para isso
• Geralmente os processos pai são daemons sendo seus filhos workers
• Você não pode alterar o SID dos filhos pois você precisa deles na mesma sessão do processo pai
�39
Spawn/exemplo.php<?php$pid = pcntl...// Fluxo normal do daemon!$childrenLimit = 10;$childrenPids = array();while (true) { if (count($childrenPids) >= $childrenLimit) { $firstChildPid = array_shift($childrenPids); pcntl_waitpid($firstChildPid, $status); }! $childPid = pcntl_fork();! if ($childPid == -1) { syslog(LOG_ERR, 'Falha ao criar filho'); continue; }! if ($childPid > 0) { $childrenPids[] = $childPid; continue; }! syslog(LOG_DEBUG, 'Envio de email iniciando'); $sent = mail('vagrant@localhost', 'Lorem ipsum', 'Lorem ipsum dolor sit amet'); if (true === $sent) { syslog(LOG_DEBUG, 'Envio de email terminado sucesso'); exit(0); } syslog(LOG_ERR, 'Falha ao enviar email'); exit(3);}closelog();
�40
ZOMBIES
�41
• Um processo zombie é um processo que já foi completamente executado mas ainda se encontra na tabela de processos do SO, permitindo que o processo que o criou leia o seu valor de saída
• Quando um processo termina, a memória a ele associada é libertada, no entanto a informação sobre esse processo continua disponível, embora ele já não exista
• Normalmente os processos zombie não duram muito tempo já que o sinal SIGCHLD é emitido quando ele entra nesse estado, possibilitando ao processo pai saber quando isso acontece para ler as informações necessárias
• Se o processo pai explicitamente ignora o SIGCHLD definindo seu manipulador como SIG_IGN todos as informações de término dos processos filhos serão descartadas e os processos zombies continuarão na tabela
�42
Zombies/reaper.php<?php!function reaper($signal){ if ($signal != SIGCHLD) { return; }! while (pcntl_waitpid(-1, $status, WNOHANG | WUNTRACED) > 0) { usleep(1000); }}!pcntl_signal(SIGCHLD, 'reaper');
�43
Inter-‐Process Communication
IPC
�44
• Cada processo possui um contexto de execução próprio. Um processo não tem conhecimento do contexto de outro processo sendo assim os processos não conseguem transferir informação entre si
• Inter-‐Process Communication (IPC), é o grupo de mecanismos que permite aos processos transferirem informação entre si
• Usando IPC um processo pai consegue obter informações precisar de seus filhos
• Para IPC podemos utilizar: ‣ Arquivos ‣ Filas de mensagens ‣ Memória Compartilhada ‣ Sinais ‣ Par de Sockets ‣ …
�45
Arquivos
• Você pode escrever dados em um processo e ler em outro processo, desde que ambos tenham permissão de leitura
• Nome do arquivo deve ser único
�46
IPC/file.php<?php!$filename = '/tmp/' . getmypid() . '.ipc';if (! is_file($filename)) { touch($filename);}!$dataWritten = 'PHP e seus Demônios';if (false === file_put_contents($filename, $dataWritten)) { echo 'Falha ao gravar dados no arquivo' . PHP_EOL; exit(2);}!$dataGiven = file_get_contents($filename);if (false === $dataGiven) { echo 'Falha ao ler dados no arquivo' . PHP_EOL; exit(3);}!echo 'Dado lido no arquivo: ' . $dataGiven . PHP_EOL;!if (! unlink($filename)) { echo 'Falha ao tentar remover o arquivo' . PHP_EOL; exit(3);}
�47
Memória compartilhada• É um fácil caminho para usar funções que permitem o PHP ler, escrever, criar e deletar segmentos de memória compartilhada UNIX
• O PHP possui duas API’s, as funções shmop_* e shm_*: • Para habilitar as funções shmop_* é preciso compilar o PHP com a opção --enable-shmop do configure
• Para habilitar as funções shm_* é preciso compilar o PHP com a opção --enable-sysvshm do configure
• Funciona basicamente com uma chave, por ela você pode ler e escrever dados na memória
• Utilize o comando ipcs para monitorar os seguimentos criados e ipcrm shm ID para remover seguimentos (você também pode usar ipcmk para criar seguimentos)
�48
IPC/shmop.php<?php!$key = getmypid();$flag = 'c';$permission = 0644;$memorySize = 1024;!$shmId = shmop_open($key, $flag, $permission, $memorySize);if (! $shmId) { echo 'Não foi possível criar o segmento de memória' . PHP_EOL; exit(1);}!$stringWritten = 'PHP e seus demônios';$shmBytesWritten = shmop_write($shmId, $stringWritten, 0);if ($shmBytesWritten != strlen($stringWritten)) { echo 'Não foi possível gravar o dado e com seu tamanho correto' . PHP_EOL; exit(2);}!$stringRead = shmop_read($shmId, 0, $memorySize);if (! $stringRead) { echo 'Não foi possível ler o dado na memória compartilhada' . PHP_EOL; exit(2);}!echo 'Dado lido na memória compartilhada foi: ' . $stringRead . PHP_EOL;!if (! shmop_delete($shmId)) { echo 'Não foi possível marcar o bloco de memória compartilhada para remoção';}!shmop_close($shmId);
�49
IPC/shm.php<?php!$key = getmypid();$permission = 0644;$memorySize = 1024;!$shmId = shm_attach($key, $memorySize, $permission);if (! $shmId) { echo 'Falha ao criar o segmento de memória' . PHP_EOL; exit(1);}!$stringWritten = 'PHP e seus demônios';if (! shm_put_var($shmId, 1, $stringWritten)) { echo 'Falha ao gravar o dado na memória compartilhada' . PHP_EOL; exit(2);}!if (! shm_has_var($shmId, 1)) { echo 'Nenhum dado na chave 1 foi encontrado na memória' . PHP_EOL; exit(2);}!$stringRead = shm_get_var($shmId, 1);if (! $stringRead) { echo 'Falha ao ler o dado da chave 1 na memória compartilhada' . PHP_EOL; exit(2);}!echo 'Dado lido na memória compartilhada foi: ' . $stringRead . PHP_EOL;!if (! shm_remove($shmId)) { echo 'Falha ao remover do bloco de memória compartilhada';}!if (! shm_detach($shmId)) { echo 'Falha ao se desconectar do bloco de memória compartilhada';}
�50
Filas de mensagens
• O PHP possui suporte a filas de mensagens do • Para habilitar as funções msg_* é preciso compilar o PHP com a opção --enable-sysvmsg do configure
• Utilize o comando ipcs para monitorar os seguimentos criados e ipcrm msg ID para remover seguimentos (você também pode usar ipcmk para criar seguimentos)
�51
IPC/msg.php<?php!$key = getmypid();$messageQueueId = msg_get_queue($key);!$messageSent = 'PHP e seus demônios';$messageWasSent = msg_send($messageQueueId, 2, $messageSent);if (! $messageWasSent) { echo 'Falha ao enviar mensagem' . PHP_EOL; exit(2);}!if (! msg_receive($messageQueueId, 2, $msgType, 1024, $messageReceived, true, 0, $error)) { echo 'Falha ao ler mensagem' . $error . PHP_EOL; exit(3);}echo 'Mensagem recebida: ' . $messageReceived . PHP_EOL;!if (! msg_remove_queue($messageQueueId)) { echo 'Falha ao remover fila de mensagens'. PHP_EOL; exit(3);}
�52
Par de sockets
• Dois sockets conectados armazenados em um array
• Conexão de duas vias, as mensagens são entregues no mesmo instante
�53
IPC/msg.php<?php!$sockets = array();!if (false === socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) { echo 'Falha ao criar par de sockets: ' . socket_strerror(socket_last_error()) . PHP_EOL;}!$pid = pcntl_fork();if ($pid == -1) { echo 'Falha na criação do fork' . PHP_EOL;} elseif ($pid > 0) {! socket_close($sockets[0]); $messageWritten = 'Mensagem enviada pelo processo pai'; if (false === socket_write($sockets[1], $messageWritten, strlen($messageWritten))) { echo 'Falha ao escrever dados no socket: ' . socket_strerror(socket_last_error($sockets)); exit(3); }! $messageGiven = socket_read($sockets[1], 1024, PHP_BINARY_READ);! echo 'Mensagem no processo pai: ' . "\t" . $messageGiven . PHP_EOL; socket_close($sockets[1]);!} else {! socket_close($sockets[1]); $messageWritten = 'Mensagem enviada pelo processo filho'; if (false === socket_write($sockets[0], $messageWritten, strlen($messageWritten))) { echo 'Falha ao escrever dados no socket: ' . socket_strerror(socket_last_error($sockets)); exit(3); }! $messageGiven = socket_read($sockets[0], 1024, PHP_BINARY_READ);! echo 'Mensagem no processo filho: ' . "\t" . $messageGiven . PHP_EOL; socket_close($sockets[0]);}
�54
Outras formas
• APC • Memcached • MongoDB • MySQL • RabbitMQ • Redis • SQLite • …
�55
PERFORMANCE
�56
• Não existe garbage collection, o processo principal não morre
• Utilize as funções gc_enable() e gc_collect_cycles()
• O PHP possui um cache padrão de arquivos abertos (em memória) isso pode prejudicar a performance do daemon, utilize clearstatcache() para remover esse cache
• Utilizar IPC sem limpar os dados corretamente pode ocasionar uma série de problemas
�57
BIBLIOTECAS
�58
• Arara\Process (https://github.com/Arara/Proccess)
• PHP-‐Daemon (https://github.com/shaneharter/PHP-‐Daemon)
• System_Daemon (http://pear.php.net/package/System_Daemon)
• ZendX_Console_Process_Unix (http://framework.zend.com/manual/1.12/en/zendx.console.process.unix.html)
�59
PERGUNTAS
�60
CONCLUSÃO
�61
Links
• @henriquemoody na maioria das redes sociais (about.me, BitBucket, Coderbits, GitHub, SlideShare, Twitter…)
• Código da palestra: https://github.com/henriquemoody/php-‐e-‐seus-‐demonios/tree/1.0.0
• Ícones: http://www.visualpharm.com • Formatação de código: https://sublime.wbond.net/packages/Highlight
�62
Referências• http://en.wikipedia.org/wiki/Cron • http://en.wikipedia.org/wiki/Daemon_(computing) • http://en.wikipedia.org/wiki/Init • http://en.wikipedia.org/wiki/POSIX • http://man7.org/linux/man-‐pages/man7/signal.7.html • http://php.net/ChangeLog-‐4.php • http://php.net/cli • http://php.net/ncurses • http://php.net/newt • http://php.net/pcntl • http://php.net/posix • http://php.net/readline • http://pt.wikipedia.org/wiki/Daemon_(computação) • http://www.slideshare.net/jkeppens/php-‐in-‐the-‐dark • http://www.win.tue.nl/~aeb/linux/lk/lk-‐10.html
�63