O que é um processo ininterrupto?

156

Às vezes, sempre que eu escrevo um programa no Linux e ele trava devido a algum tipo de bug, ele se torna um processo ininterrupto e continua em execução para sempre até eu reiniciar o computador (mesmo se eu sair). Minhas perguntas são:

  • O que faz com que um processo se torne ininterrupto?
  • Como faço para impedir que isso aconteça?
  • Essa provavelmente é uma pergunta idiota, mas existe alguma maneira de interrompê-la sem reiniciar o computador?
Jason Baker
fonte
É possível que um programa possa ser gravado para iniciar um processo que entra em um TASK_UNINTERUPTIBLEestado sempre que o sistema não está em um estado ocioso, coletando dados à força, aguardando a transmissão assim que o superusuário sai? Isso seria uma mina de ouro para hackers recuperar informações, retornar ao estado de zumbi e transmitir informações pela rede em modo inativo. Alguns podem argumentar que essa é uma maneira de criar um Blackdoorpara os poderes existentes, de entrar e sair de qualquer sistema, conforme desejado. Eu acredito firmemente que essa brecha pode ser selada definitivamente, eliminando o `TASK_UNINTERUPTIB
Nuuwski
2
seria por favor compartilhe o código?
novamente

Respostas:

198

Um processo ininterrupto é um processo que ocorre em uma chamada do sistema (função kernel) que não pode ser interrompida por um sinal.

Para entender o que isso significa, você precisa entender o conceito de uma chamada de sistema interrompível. O exemplo clássico é read(). Essa é uma chamada do sistema que pode demorar muito tempo (segundos), pois pode envolver girar um disco rígido ou mover cabeças. Durante a maior parte desse tempo, o processo ficará inativo, bloqueando o hardware.

Enquanto o processo está inativo na chamada do sistema, ele pode receber um sinal assíncrono Unix (por exemplo, SIGTERM), e acontece o seguinte:

  • O sistema chama saídas prematuramente e é configurado para retornar -EINTR ao espaço do usuário.
  • O manipulador de sinal é executado.
  • Se o processo ainda estiver em execução, ele obtém o valor de retorno da chamada do sistema e pode fazer a mesma chamada novamente.

Retornar cedo da chamada do sistema permite que o código de espaço do usuário altere imediatamente seu comportamento em resposta ao sinal. Por exemplo, terminando de forma limpa em reação ao SIGINT ou SIGTERM.

Por outro lado, algumas chamadas do sistema não podem ser interrompidas dessa maneira. Se o sistema chamar paradas por algum motivo, o processo poderá permanecer indefinidamente nesse estado inábil.

O LWN publicou um bom artigo que abordou esse tópico em julho.

Para responder à pergunta original:

  • Como evitar que isso aconteça: descubra qual driver está causando problemas e pare de usar ou torne-se um hacker de kernel e corrija-o.

  • Como matar um processo ininterrupto sem reiniciar: de alguma forma, faça a chamada do sistema terminar. Freqüentemente, a maneira mais eficaz de fazer isso sem pressionar o botão liga / desliga é puxar o cabo de alimentação. Você também pode se tornar um hacker de kernel e fazer com que o driver use TASK_KILLABLE, conforme explicado no artigo LWN.

ddaa
fonte
31
Puxei o cabo de alimentação do meu laptop e infelizmente não está funcionando. ;-)
thecarpy 19/02
1
Não é EINTR em vez de EAGAIN? Também read () retorna -1 e errno é definido como erro.
Lethalman
2
@Exter: Você está realmente perdendo o ponto. Leia o artigo do LWN: lwn.net/Articles/288056 . Esses problemas são causados ​​por programadores de driver de dispositivo lento e precisam ser corrigidos no código do driver de dispositivo.
Ddaa
4
@ddaa "A tradição Unix (e, portanto, quase todas as aplicações) acredita que as gravações no armazenamento de arquivos não são interrompidas por sinal. Não seria seguro ou prático alterar essa garantia." -> Esta é exatamente a parte mais errada de toda essa IMO. Basta interromper a solicitação de leitura / gravação do driver e, quando o dispositivo real (disco rígido / placa de rede / etc) entregar os dados, ignore-os. Um kernel do sistema operacional deve ser criado de forma que nenhum desenvolvedor possa estragar tudo.
Dexter
2
@ddaa Eu sei que o Linux não é um microkernel, embora não tenha certeza de qual parte do meu comentário está relacionada a ele ... E então, o seu comentário significa que um sistema operacional de microkernel não tem problemas com esses processos "ininterruptos"? Porque se isso não acontecer, talvez seja a hora de me tornar um fã de microkernel ...: D
Dexter
49

Quando um processo está no modo de usuário, ele pode ser interrompido a qualquer momento (alternando para o modo kernel). Quando o kernel retorna ao modo de usuário, ele verifica se há algum sinal pendente (incluindo os que são usados ​​para interromper o processo, como SIGTERMe SIGKILL). Isso significa que um processo pode ser eliminado apenas ao retornar ao modo de usuário.

A razão pela qual um processo não pode ser eliminado no modo kernel é que ele pode potencialmente corromper as estruturas do kernel usadas por todos os outros processos na mesma máquina (da mesma forma que matar um encadeamento pode potencialmente corromper as estruturas de dados usadas por outros encadeamentos no mesmo processo) .

Quando o kernel precisa fazer algo que pode levar muito tempo (aguardando um pipe escrito por outro processo ou aguardando o hardware fazer algo, por exemplo), ele dorme marcando-se como adormecido e chamando o agendador para mudar para outro processo (se não houver um processo que não seja adormecido, ele alterna para um processo "fictício" que instrui a CPU a desacelerar um pouco e fica em um loop - o loop inativo).

Se um sinal é enviado para um processo adormecido, ele deve ser acordado antes de retornar ao espaço do usuário e, assim, processar o sinal pendente. Aqui temos a diferença entre os dois principais tipos de sono:

  • TASK_INTERRUPTIBLE, o sono interrompível. Se uma tarefa estiver marcada com esse sinalizador, ela estará em suspensão, mas poderá ser despertada por sinais. Isso significa que o código que marcou a tarefa como inativa está esperando um possível sinal e, depois que acordar, procurará por ele e retornará da chamada do sistema. Depois que o sinal é tratado, a chamada do sistema pode potencialmente ser reiniciada automaticamente (e não entrarei em detalhes sobre como isso funciona).
  • TASK_UNINTERRUPTIBLE, o sono ininterrupto. Se uma tarefa estiver marcada com esse sinalizador, ela não espera ser acordada por nada além do que está esperando, porque ela não pode ser reiniciada facilmente ou porque os programas esperam que a chamada do sistema seja atômica. Isso também pode ser usado para dorme conhecido por ser muito curto.

TASK_KILLABLE (mencionado no artigo do LWN vinculado pela resposta da ddaa) é uma nova variante.

Isso responde à sua primeira pergunta. Quanto à sua segunda pergunta: você não pode evitar interrupções ininterruptas, elas são normais (acontece, por exemplo, toda vez que um processo lê / grava no / para o disco); no entanto, eles devem durar apenas uma fração de segundo. Se eles duram muito mais, geralmente significa um problema de hardware (ou um problema de driver de dispositivo, que parece o mesmo com o kernel), em que o driver de dispositivo aguarda o hardware fazer algo que nunca acontecerá. Também pode significar que você está usando o NFS e o servidor NFS está inoperante (está aguardando a recuperação do servidor; você também pode usar a opção "intr" para evitar o problema).

Finalmente, o motivo pelo qual você não pode se recuperar é o mesmo motivo pelo qual o kernel aguarda até retornar ao modo de usuário para emitir um sinal ou interromper o processo: ele potencialmente corromperia as estruturas de dados do kernel (o código aguardando um sono interrompido pode receber um erro que informa para retornar ao espaço do usuário, onde o processo pode ser interrompido; o código aguardando um sono ininterrupto não espera nenhum erro).

CesarB
fonte
1
O bug de bloqueio do sistema de arquivos também é uma causa provável, IME.
Tobu
3
Eu não entendo tudo isso. "você não pode evitar sonolências ininterruptas" - o sistema operacional não pode ser criado de tal maneira que o sono ininterrupto simplesmente não exista como estado? Então a parte sobre corrupção - não pode a parte do processo em modo kernel (ou o que PODE causar a corrupção) ser encerrada ou apenas seu código modificado na memória para retornar? Por favor, explique por que isso é tão difícil / impossível de fazer que nem o Linux fez. (Eu pensei que este problema existe apenas no Windows)
Dexter
O único caso que eu posso pensar que faria (com segurança) matando esses processos realmente impossível (e não apenas, digamos, excepcionalmente difícil) é se o hardware em si poderia causar a corrupção. O hardware não pode ser controlado; kernel pode . Mas é o kernel que obtém dados do hardware e modifica a memória (é por isso que não deve ser liberado antes que o processo retorne ao modo de usuário e por que a corrupção pode ocorrer) ... altere o código do kernel na memória e sem mais problemas.
Dexter
O @Dexter pensa no kernel como se fosse um único processo multiencadeado, onde a parte do modo kernel de cada processo é um encadeamento dentro do kernel. Sua sugestão seria tão ruim quanto matar um único encadeamento em um programa com vários encadeamentos: poderia deixar bloqueios pendentes, estruturas de dados temporariamente modificadas ou no meio da modificação, e assim por diante.
CesarB #
@ CesarB bem, você está certo em matar um thread ... Mas o thread "main" (que seria o kernel do sistema operacional e outros threads seriam drivers por exemplo) não consegue lidar com isso de alguma forma? Embora essas estruturas "no meio de ser modificado" parece ser uma questão muito difícil ... talvez nós realmente nunca verá um sistema operacional onde os processos ininterrupta seria impossível :(
Dexter
23

Processos ininterruptos geralmente aguardam E / S após uma falha na página.

Considere isto:

  • O encadeamento tenta acessar uma página que não está no núcleo (um executável que é carregado por demanda, uma página de memória anônima que foi trocada ou um arquivo mmap () 'd que é carregado por demanda, que é muito mais mesma coisa)
  • O kernel agora está (tentando) carregá-lo no
  • O processo não pode continuar até que a página esteja disponível.

O processo / tarefa não pode ser interrompido nesse estado, porque não pode manipular nenhum sinal; se isso acontecesse, outra falha de página ocorreria e estaria de volta onde estava.

Quando digo "processo", quero dizer realmente "tarefa", que no Linux (2.6) traduz aproximadamente para "thread", que pode ou não ter uma entrada individual de "grupo de threads" em / proc

Em alguns casos, pode demorar um longo tempo. Um exemplo típico disso seria onde o arquivo executável ou mmap está em um sistema de arquivos de rede em que o servidor falhou. Se a E / S tiver êxito, a tarefa continuará. Se, eventualmente, falhar, a tarefa geralmente receberá um SIGBUS ou algo assim.

MarkR
fonte
1
Se, eventualmente, falhar, a tarefa geralmente receberá um SIGBUS ou algo assim. Espere, o kernel não pode ser feito para que, ao eliminar esses processos "ininterruptos", simplesmente informe que a operação de E / S falhou? Então o processo retornaria ao modo de usuário e desapareceria? Tem que haver uma maneira de matar com segurança os processos do estado 'D'. Eu acho que não é fácil e é por isso que nem o Windows nem o Linux têm essa possibilidade ainda. Por outro lado, eu gostaria de poder matar esses processos pelo menos sem segurança. Eu não ligo para uma possível falha no sistema ou o que quer ...
Dexter
@ Dexter hmm, eu nunca tive esse problema com o Windows. Qual é a maneira de reproduzi-lo lá? Pelo menos de acordo com esta postagem , todas as solicitações de E / S podem ser interrompidas no Windows.
Ruslan
1

Para sua terceira pergunta: eu acho que você pode matar os processos ininterruptos executando sudo kill -HUP 1. Ele reiniciará o init sem encerrar os processos em execução e, após executá-lo, meus processos ininterruptos se foram.

Ron Granger
fonte
-3

Se você está falando sobre um processo "zumbi" (que é designado como "zumbi" na saída ps), esse é um registro inofensivo na lista de processos, à espera de alguém para coletar seu código de retorno e pode ser ignorado com segurança.

Você poderia descrever o que é um "processo ininterrupto" para você? Ele sobrevive ao "kill -9" e se diverte alegremente? Se esse for o caso, ele ficará preso em algum syscall, que está preso em algum driver, e você ficará preso nesse processo até a reinicialização (e às vezes é melhor reiniciar em breve) ou descarregar o driver relevante (o que é improvável) . Você pode tentar usar "strace" para descobrir onde seu processo está travado e evitá-lo no futuro.

ADEpt
fonte
Os drivers não podem ser descarregados à força da mesma maneira que um processo pode ser morto? Eu sei que o modo kernel tem acesso mais privilegiado que o modo usuário, mas nunca pode ser mais privilegiado que o próprio sistema operacional. Qualquer coisa executada no modo kernel pode sempre alterar qualquer outra coisa executada no modo kernel - simplesmente não há controle.
Dexter