Unix Process
Todos vocês já usaram...
Tudo isso está
sendo executado
através de um
PROCESSO
Um pouco de história
- A programação unix existe desde 1970
- Foi inventado nos laboratórios Bell (Bell labs)
- Os conceitos e técnicas de programação unix
não são novidades. Essas técnicas vão além das
linguagens de programação.
- Essas técnicas não são modificadas há décadas
Processos
- Quem sabe a diferença entre userland e kernel?
Processos
Kernel
- É a camada que está em cima do hardware. Ou
seja, é o homem do meio entre toda interação que
acontece entre a userland e o hardware
- Essas interações incluem coisas como:
. Ler/Escrever no diretório de arquivos
. Enviar arquivos pela rede
. Alocar memória
. Tocar música através dos auto-falantes
Devido ao seu poder, nenhum programa possui
acesso direto ao Kernel
Processos
Userland
- É onde todos os seus programas são executados
- Seu programa pode fazer muita coisa sem
precisar do kernel:
. Realizar operações matemáticas
. Realizar operações com strings
. Controle de fluxo através de operações lógicas
Mas se você quiser fazer várias coisas legais terá
que utilizar o kernel
Processos
System call
- Qualquer comunicação entre o kernel e userland
é feita através de system calls
- É a interface que conecta o kernel e a userland
- Define as interações que são permitidas entre o
seu programa e o hardware
Processos
Process identifier (pid)
- Todo processo possui um identificador único
- Nada mais é que um número sequencial. É assim
que o kernel vê o seu processo: como um número
>> puts Process.pid || $$ #system call: getpid
- Não está ligado a nenhum aspecto do conteúdo
do processo.
- Pode ser entendido por qualquer linguagem de
programação
Processos
Parents process (ppid)
- Todo processo rodando no seu sistema possui
um pai
- O processo pai é o processo que invocou outro
determinado processo.
>> puts Process.ppid #system call: getppid
- O ppid pode ser importante quando estivermos
detectando processos deamons
Processos
File descriptors
- Representa arquivos abertos
- Em unix tudo é arquivo!
. Isso significa que dispositivos são tratados como
arquivo, sockets e pipes são tratados como
arquivos e arquivos são tratados como arquivos!
- Qualquer momento que você abrir um recurso, é
atribuido um número de file descriptor a esse
recurso
Processos
File descriptors
- Não são compartilhados entre processos que não
estão relacionados
- Eles vivem e morrem com o processo que eles
estão ligados
>> file = File.open(“path/to/the/file”)
>> file.fileno
=> 3
- É através do fileno que o kernel consegue manter
o controle dos recursos que seu processo está
utilizando
Processos
File descriptors
- Apenas recursos abertos possuem file
descriptors
- Todo processo unix vem com três recursos
abertos
. STDIN
. STDOUT
. STDERR
- Existe uma limitação de 1024 file descriptors
abertos por processo
Processos
Environment variables
- São pares chave-valor que possuem dados para
os processos
- Todo processo herda variáveis de ambiente de
seus pais
- São definidas por processo e são globais para
cada processo
$ MESS=‘teste’ ruby -e “puts ENV[‘MESS’]”
Processos
Argumentos
- ARGV: argument vector
- É uma forma de passar argumentos para os
processos
p ARGV
$ ruby argv.rb foo bar -ba
[“foo”, “bar”, “-ba”]
Processos
Possuem nome
- Todo processo possue um nome e esse nome
pode ser mudado durante seu runtime
>> puts $PROGRAM_NAME
- É uma forma eficiente de comunicar a tarefa que
está sendo desenvolvida
=> irb
Processos
Possuem um código de finalização
- Todo processo, ao finalizar, possui um código
- É um número entre 0-255 que denota se o
processo finalizou com sucesso ou não
- A finalização com um código 0 indica que tudo
ocorreu como esperado
- Mas... Os outros códigos são utilizados como
forma de comunicação
Proccess
CAN use the
FORK
Processos
Fork
- É um dos conceitos mais poderosos da
programação unix
- Permite que um processo em execução crie outro
processo, utilizando recursos de programação
para isso
- O novo processo criado é uma cópia exata do
processo original
Processos
Fork
- O processo filho herda uma cópia de toda
memória usada pelo processo pai bem como todos
os file descriptors
Recapitulando...
- O ppid do processo filho é o pid do processo pai.
- O processo filho possui o mesmo mapa de file
descriptors que o processo pai
Processos
Fork
if fork
puts “entrando no if”
else
puts “entrando no else”
end
=> entrando no if
=> entrando no else
WTF???
- O método fork retorna duas vezes (ele cria outro
processo, lembra??)
- Retorna uma vez no processo pai e uma vez no
processo filho
Processos
Fork
puts “processo pai #{Process.pid}”
if fork
puts “entrando no if atraves do processo #{Process.pid}”
else
puts “entrando no else atraves do processo #{Process.pid}”
end
=> processo pai 2000
=> entrando no if atraves do processo 2000
=> entrando no else atraves do processo 2010
- O processo filho é finalizado após executar o
código que está no else
- O retorno do fork no processo pai é o pid do filho.
Já no processo filho o fork retorna nil
Processos
Fork
Multicore Programming
- Não é garantido de ser distribuido através das
CPUs disponíveis. Depende do SO
- É preciso ser feito com cuidado!
# system call => fork
Processos
Processos orfãos
fork do
5.times do
sleep 1
puts “Eu sou orfão!”
end
end
abort “Parent process died ...”
- O que acontece com um processo filho quando
seu pai morre?
. Nada!
Processos
CoW Friendly
- Copiar toda memória na hora do fork pode ser
considerado um overhead
- Sistemas Unix modernos empregam uma técnica
chamada Copy on Write para evitar o overhead
- A cópia da memória é evitada até que alguma
escrita seja necessária
- Dessa forma o processo pai e o processo filho
compartilham a memória até uma escrita ser
realizada pelo processo filho
Processos
CoW Friendly
arr = [1,2,4]
fork do
# nesse momento não há copia de memória
# a leitura é feita de forma compartilhada
puts arr
end
fork do
# Ao modificar o array uma cópia do array precisa ser feita
# para o processo filho
arr << 4
end
- Isso mostra que realizar um fork é rápido
- Os processos filhos possuem uma cópia dos
dados modificados. O resto pode ser
compartilhado
Processos
CoW Friendly
- MRI não é CoW friendly
. O garbage collector utiliza um algoritmo ‘mark-
and-sweep’ que não é compatível com cow
. O algoritmo realiza uma iteração por todo objeto
existente marcando se o mesmo deve ser coletado
ou não
. Dessa forma, quando o garbage collector rodar
toda memória sera copiada para o processo filho
Processos
Podem esperar
- Realizar um fork e não esperar o resultado é
interessante para tarefas assincronas
message = “Opa, bão?”
recipient = “rodrigo.diley@dito.com.br”
fork do
StatsCollector.record message, recipient
end
#continua o envio da mensagem
- Mas em alguns outros casos você quer manter o
controle sobre seus processos filhos
Processos
Podem esperar
fork do
5.times do
sleep 1
puts “Eu sou orfão!”
end
end
- Basta utilizar em ruby: Process.wait
Process.wait
abort “Processo pai morreu ...”
=> “Eu sou orfão!”
=> “Eu sou orfão!”
=> “Eu sou orfão!”
=> “Eu sou orfão!”
=> “Eu sou orfão!”
=> “Processo pai morreu ...”
Podem esperar
- Process.wait
Processos
. É uma chamada bloqueante que faz o processo
pai esperar por um de seus filhos terminar a
execução
. É você que precisa saber qual processo está
sendo terminado. Process.wait retorna o pid do
processo filho
Processos
Podem esperar
- Condições de corrida
. O que acontece quando um processo pai demora
para realizar a chamada ao metodo wait?
. O Kernel coloca todas as informações de exit dos
processos filhos em uma fila!
. Se Process.wait for chamado e não existir
processo filho será lançada uma exception
Processos
Zombieee
- É sempre bom limpar os processos filhos
- O Kernel guarda informações de todos os
processos filhos em um fila, estão lembrados?
- Se nós não retirarmos ela de lá, quem irá?
NINGUEM
- Estamos gastando mal os recursos do Kernel
Processos
Zombieee
message = “Opa, bão?”
recipient = “rodrigo.diley@dito.com.br”
pid = fork do
StatsCollector.record message, recipient
end
Process.detach(pid)
#continua o envio da mensagem
- Arrumando nosso exemplo
- Basicamente, Process.detach dispara uma nova
thread que terá como terefa esperar o processo
filho terminar
Processos
Zombieee
- Todo processo filho que termina e não possui
seus status coletado é considerado zombie
pid = fork { sleep(1) }
puts pid
sleep
$ ps -ho pid,state -p [pid do processo]
- O status impresso será z ou Z+
Processos
Recebem sinais
- Process.wait é uma chamada bloqueante
- Como esperar por processos filhos em processos
pais que estão sempre ocupados?
. Basta trabalhar com sinais!
. CHLD é o sinal que o kernel envia para o
processo pai indicando que um processo filho foi
finalizado
Processos
Recebem sinais
child_process = 3
dead_process = 0
child_process.times do
fork do
sleep 3
end
end
trap(:CHLD) do
while pid = Process.wait(-1, Process::WNOHANG)
puts pid
dead_process += 1
exit if dead_process == child_process
end
end
loop do
(Math.sqrt(rand(44)) ** 8).floor
sleep 1
end
Processos
Recebem sinais
- É uma forma de comunicação assincrona
- Quando um processo recebe um sinal do Kernel
as seguintes ações podem ser tomadas:
. Ignorar
. Realizar algum comportamento específico
. Realizar o comportamento padrão
Processos
Recebem sinais
- Os sinais são enviados através do Kernel
- Basicamente, o Kernel é o middleman utilizado
pelos processos para enviarem sinais uns aos
outros
Computador
LIGADO rodando
task remota
NUNCA MAIS
Processos
Recebem sinais
- Se sua tarefa já estiver rodando em um console
basta enviar o sinal de stop para que a tarefa seja
interrompida
- Após sua interrupção basta colocá-la para rodar
em background através do comando bg
- Basta remover o vínculo com o terminal através
do comando disown
Processos
Podem se comunicar
- Existem algumas formas de realizar IPC (Inter-
process communication)
. Pipe
. Sockets
Processos
Podem se comunicar
- Pipe
. É um caminho de mão única de fluxo de dados
reader, writer = IO.pipe => [#<IO:fd 5>, #<IO:fd 6>]
writer.write(“Galo doido não perde em casa”)
writer.close
puts reader.read
=> “Galo doido não perde em casa”
. É um stream de dados!
Processos
Podem se comunicar
- Pipe
. É um recurso compartilhado como qualquer
outro
reader, writer = IO.pipe => [#<IO:fd 5>, #<IO:fd 6>]
fork do
reader.close
10.times do
#trabalhando...
writer.puts “Quase acabando”
end
end
writer.close
while message = reader.gets
$stdout.puts message
end
Processos
Podem se comunicar
- Sockets
. Pode ser um socket unix, para comunicação
local
. Pode ser um socket tcp, para comunicação
remota
- Outra possível solução seria RPC
Processos
Deamon
- São processos que rodam em background e que
não estão ligados a nenhum terminal controlado
pelo usuário
- Tem um processo deamon que é muito
importante para o sistema operacional
. É o pai de todos. É o processo chamado init, seu
ppid é 0 e o seu pid é 1
Processos
Deamon
- Process group e session group
$ git log | grep shipped | less
- Todo sinal enviado para o pai de um process
group é encaminhado para os filhos
- Todo sinal encaminhado para um session group
é encaminhado para os processos que fazem parte
desse grupo
OBRIGADO!
@sergiohenrique
sergio.miranda@dito.com.br

Unix Process

  • 1.
  • 2.
  • 3.
    Tudo isso está sendoexecutado através de um PROCESSO
  • 4.
    Um pouco dehistória - A programação unix existe desde 1970 - Foi inventado nos laboratórios Bell (Bell labs) - Os conceitos e técnicas de programação unix não são novidades. Essas técnicas vão além das linguagens de programação. - Essas técnicas não são modificadas há décadas
  • 5.
    Processos - Quem sabea diferença entre userland e kernel?
  • 6.
    Processos Kernel - É acamada que está em cima do hardware. Ou seja, é o homem do meio entre toda interação que acontece entre a userland e o hardware - Essas interações incluem coisas como: . Ler/Escrever no diretório de arquivos . Enviar arquivos pela rede . Alocar memória . Tocar música através dos auto-falantes Devido ao seu poder, nenhum programa possui acesso direto ao Kernel
  • 7.
    Processos Userland - É ondetodos os seus programas são executados - Seu programa pode fazer muita coisa sem precisar do kernel: . Realizar operações matemáticas . Realizar operações com strings . Controle de fluxo através de operações lógicas Mas se você quiser fazer várias coisas legais terá que utilizar o kernel
  • 8.
    Processos System call - Qualquercomunicação entre o kernel e userland é feita através de system calls - É a interface que conecta o kernel e a userland - Define as interações que são permitidas entre o seu programa e o hardware
  • 9.
    Processos Process identifier (pid) -Todo processo possui um identificador único - Nada mais é que um número sequencial. É assim que o kernel vê o seu processo: como um número >> puts Process.pid || $$ #system call: getpid - Não está ligado a nenhum aspecto do conteúdo do processo. - Pode ser entendido por qualquer linguagem de programação
  • 10.
    Processos Parents process (ppid) -Todo processo rodando no seu sistema possui um pai - O processo pai é o processo que invocou outro determinado processo. >> puts Process.ppid #system call: getppid - O ppid pode ser importante quando estivermos detectando processos deamons
  • 11.
    Processos File descriptors - Representaarquivos abertos - Em unix tudo é arquivo! . Isso significa que dispositivos são tratados como arquivo, sockets e pipes são tratados como arquivos e arquivos são tratados como arquivos! - Qualquer momento que você abrir um recurso, é atribuido um número de file descriptor a esse recurso
  • 12.
    Processos File descriptors - Nãosão compartilhados entre processos que não estão relacionados - Eles vivem e morrem com o processo que eles estão ligados >> file = File.open(“path/to/the/file”) >> file.fileno => 3 - É através do fileno que o kernel consegue manter o controle dos recursos que seu processo está utilizando
  • 13.
    Processos File descriptors - Apenasrecursos abertos possuem file descriptors - Todo processo unix vem com três recursos abertos . STDIN . STDOUT . STDERR - Existe uma limitação de 1024 file descriptors abertos por processo
  • 14.
    Processos Environment variables - Sãopares chave-valor que possuem dados para os processos - Todo processo herda variáveis de ambiente de seus pais - São definidas por processo e são globais para cada processo $ MESS=‘teste’ ruby -e “puts ENV[‘MESS’]”
  • 15.
    Processos Argumentos - ARGV: argumentvector - É uma forma de passar argumentos para os processos p ARGV $ ruby argv.rb foo bar -ba [“foo”, “bar”, “-ba”]
  • 16.
    Processos Possuem nome - Todoprocesso possue um nome e esse nome pode ser mudado durante seu runtime >> puts $PROGRAM_NAME - É uma forma eficiente de comunicar a tarefa que está sendo desenvolvida => irb
  • 17.
    Processos Possuem um códigode finalização - Todo processo, ao finalizar, possui um código - É um número entre 0-255 que denota se o processo finalizou com sucesso ou não - A finalização com um código 0 indica que tudo ocorreu como esperado - Mas... Os outros códigos são utilizados como forma de comunicação
  • 18.
  • 19.
    Processos Fork - É umdos conceitos mais poderosos da programação unix - Permite que um processo em execução crie outro processo, utilizando recursos de programação para isso - O novo processo criado é uma cópia exata do processo original
  • 20.
    Processos Fork - O processofilho herda uma cópia de toda memória usada pelo processo pai bem como todos os file descriptors Recapitulando... - O ppid do processo filho é o pid do processo pai. - O processo filho possui o mesmo mapa de file descriptors que o processo pai
  • 21.
    Processos Fork if fork puts “entrandono if” else puts “entrando no else” end => entrando no if => entrando no else WTF??? - O método fork retorna duas vezes (ele cria outro processo, lembra??) - Retorna uma vez no processo pai e uma vez no processo filho
  • 22.
    Processos Fork puts “processo pai#{Process.pid}” if fork puts “entrando no if atraves do processo #{Process.pid}” else puts “entrando no else atraves do processo #{Process.pid}” end => processo pai 2000 => entrando no if atraves do processo 2000 => entrando no else atraves do processo 2010 - O processo filho é finalizado após executar o código que está no else - O retorno do fork no processo pai é o pid do filho. Já no processo filho o fork retorna nil
  • 23.
    Processos Fork Multicore Programming - Nãoé garantido de ser distribuido através das CPUs disponíveis. Depende do SO - É preciso ser feito com cuidado! # system call => fork
  • 24.
    Processos Processos orfãos fork do 5.timesdo sleep 1 puts “Eu sou orfão!” end end abort “Parent process died ...” - O que acontece com um processo filho quando seu pai morre? . Nada!
  • 25.
    Processos CoW Friendly - Copiartoda memória na hora do fork pode ser considerado um overhead - Sistemas Unix modernos empregam uma técnica chamada Copy on Write para evitar o overhead - A cópia da memória é evitada até que alguma escrita seja necessária - Dessa forma o processo pai e o processo filho compartilham a memória até uma escrita ser realizada pelo processo filho
  • 26.
    Processos CoW Friendly arr =[1,2,4] fork do # nesse momento não há copia de memória # a leitura é feita de forma compartilhada puts arr end fork do # Ao modificar o array uma cópia do array precisa ser feita # para o processo filho arr << 4 end - Isso mostra que realizar um fork é rápido - Os processos filhos possuem uma cópia dos dados modificados. O resto pode ser compartilhado
  • 27.
    Processos CoW Friendly - MRInão é CoW friendly . O garbage collector utiliza um algoritmo ‘mark- and-sweep’ que não é compatível com cow . O algoritmo realiza uma iteração por todo objeto existente marcando se o mesmo deve ser coletado ou não . Dessa forma, quando o garbage collector rodar toda memória sera copiada para o processo filho
  • 28.
    Processos Podem esperar - Realizarum fork e não esperar o resultado é interessante para tarefas assincronas message = “Opa, bão?” recipient = “rodrigo.diley@dito.com.br” fork do StatsCollector.record message, recipient end #continua o envio da mensagem - Mas em alguns outros casos você quer manter o controle sobre seus processos filhos
  • 29.
    Processos Podem esperar fork do 5.timesdo sleep 1 puts “Eu sou orfão!” end end - Basta utilizar em ruby: Process.wait Process.wait abort “Processo pai morreu ...” => “Eu sou orfão!” => “Eu sou orfão!” => “Eu sou orfão!” => “Eu sou orfão!” => “Eu sou orfão!” => “Processo pai morreu ...”
  • 30.
    Podem esperar - Process.wait Processos .É uma chamada bloqueante que faz o processo pai esperar por um de seus filhos terminar a execução . É você que precisa saber qual processo está sendo terminado. Process.wait retorna o pid do processo filho
  • 31.
    Processos Podem esperar - Condiçõesde corrida . O que acontece quando um processo pai demora para realizar a chamada ao metodo wait? . O Kernel coloca todas as informações de exit dos processos filhos em uma fila! . Se Process.wait for chamado e não existir processo filho será lançada uma exception
  • 32.
    Processos Zombieee - É semprebom limpar os processos filhos - O Kernel guarda informações de todos os processos filhos em um fila, estão lembrados? - Se nós não retirarmos ela de lá, quem irá? NINGUEM - Estamos gastando mal os recursos do Kernel
  • 33.
    Processos Zombieee message = “Opa,bão?” recipient = “rodrigo.diley@dito.com.br” pid = fork do StatsCollector.record message, recipient end Process.detach(pid) #continua o envio da mensagem - Arrumando nosso exemplo - Basicamente, Process.detach dispara uma nova thread que terá como terefa esperar o processo filho terminar
  • 34.
    Processos Zombieee - Todo processofilho que termina e não possui seus status coletado é considerado zombie pid = fork { sleep(1) } puts pid sleep $ ps -ho pid,state -p [pid do processo] - O status impresso será z ou Z+
  • 35.
    Processos Recebem sinais - Process.waité uma chamada bloqueante - Como esperar por processos filhos em processos pais que estão sempre ocupados? . Basta trabalhar com sinais! . CHLD é o sinal que o kernel envia para o processo pai indicando que um processo filho foi finalizado
  • 36.
    Processos Recebem sinais child_process =3 dead_process = 0 child_process.times do fork do sleep 3 end end trap(:CHLD) do while pid = Process.wait(-1, Process::WNOHANG) puts pid dead_process += 1 exit if dead_process == child_process end end loop do (Math.sqrt(rand(44)) ** 8).floor sleep 1 end
  • 37.
    Processos Recebem sinais - Éuma forma de comunicação assincrona - Quando um processo recebe um sinal do Kernel as seguintes ações podem ser tomadas: . Ignorar . Realizar algum comportamento específico . Realizar o comportamento padrão
  • 38.
    Processos Recebem sinais - Ossinais são enviados através do Kernel - Basicamente, o Kernel é o middleman utilizado pelos processos para enviarem sinais uns aos outros
  • 39.
  • 40.
    Processos Recebem sinais - Sesua tarefa já estiver rodando em um console basta enviar o sinal de stop para que a tarefa seja interrompida - Após sua interrupção basta colocá-la para rodar em background através do comando bg - Basta remover o vínculo com o terminal através do comando disown
  • 41.
    Processos Podem se comunicar -Existem algumas formas de realizar IPC (Inter- process communication) . Pipe . Sockets
  • 42.
    Processos Podem se comunicar -Pipe . É um caminho de mão única de fluxo de dados reader, writer = IO.pipe => [#<IO:fd 5>, #<IO:fd 6>] writer.write(“Galo doido não perde em casa”) writer.close puts reader.read => “Galo doido não perde em casa” . É um stream de dados!
  • 43.
    Processos Podem se comunicar -Pipe . É um recurso compartilhado como qualquer outro reader, writer = IO.pipe => [#<IO:fd 5>, #<IO:fd 6>] fork do reader.close 10.times do #trabalhando... writer.puts “Quase acabando” end end writer.close while message = reader.gets $stdout.puts message end
  • 44.
    Processos Podem se comunicar -Sockets . Pode ser um socket unix, para comunicação local . Pode ser um socket tcp, para comunicação remota - Outra possível solução seria RPC
  • 45.
    Processos Deamon - São processosque rodam em background e que não estão ligados a nenhum terminal controlado pelo usuário - Tem um processo deamon que é muito importante para o sistema operacional . É o pai de todos. É o processo chamado init, seu ppid é 0 e o seu pid é 1
  • 46.
    Processos Deamon - Process groupe session group $ git log | grep shipped | less - Todo sinal enviado para o pai de um process group é encaminhado para os filhos - Todo sinal encaminhado para um session group é encaminhado para os processos que fazem parte desse grupo
  • 47.