Como verificar qual limite foi excedido? (Processo encerrado devido a ulimit.)

11

Vamos supor que o processo seja executado em um ambiente limitado:

(
ulimit  ... -v ... -t ... -x 0 ...
./program
)

O programa é encerrado.

Pode haver vários motivos: limite de memória / hora / arquivo excedido; apenas segfault simples; ou mesmo terminação normal com o código de retorno 0.

Como verificar qual foi o motivo do encerramento do programa, sem modificar o programa?

PS Quero dizer "quando o binário é dado". Talvez algum wrapper (ptrace-ing etc) possa ajudar?

Grzegorz Wierzowiecki
fonte

Respostas:

6

De um modo geral, acho que você não pode, infelizmente. (Alguns sistemas operacionais podem fornecer isso, mas não conheço os que conheço suportando isso.)

Documento de referência para limites de recursos: getrlimitdo POSIX 2008.

Tomemos, por exemplo, o limite da CPU RLIMIT_CPU.

  • Se o processo exceder o limite flexível, será enviado um SIGXCPU
  • Se o processo exceder o limite rígido, obtém-se uma SIGKILL

Se você pode wait()no seu programa, você pode dizer se ele foi morto por SIGXCPU. Mas você não pode diferenciar um SIGKILLdespachado por violação do limite rígido de uma simples e antiga matança de fora. Além do mais, se o programa lidar com isso XCPU, você nem verá isso de fora.

A mesma coisa para RLIMIT_FSIZE. Você pode ver a SIGXFSZpartir do wait()status se o programa não lidar com isso. Mas quando o limite de tamanho do arquivo é excedido, a única coisa que acontece é que mais E / S que tenta testar esse limite novamente simplesmente recebe EFBIG- isso será tratado (ou não, infelizmente) pelo programa internamente. Se o programa funcionar SIGXFSZ, o mesmo que acima - você não saberá.

RLIMIT_NOFILE? Bem, você nem recebe sinal. opene amigos apenas retornam EMFILEao programa. Caso contrário, não será incomodado, portanto falhará (ou não) da maneira que foi codificado para falhar nessa situação.

RLIMIT_STACK? Bom velho SIGSEGV, não pode ser distinguido da pontuação de outros motivos para ser entregue um. (Você saberá que foi isso que matou o processo, a partir do waitstatus.)

RLIMIT_ASe RLIMIT_DATAapenas fará malloc()e alguns outros começarão a falhar (ou receberão SIGSEGVse o limite do AS for atingido ao tentar estender a pilha no Linux). A menos que o programa seja muito bem escrito, provavelmente falhará aleatoriamente nesse ponto.

Portanto, em resumo, geralmente, as falhas não são visivelmente diferentes de outros motivos de morte do processo, portanto, você não pode ter certeza ou pode ser tratado inteiramente a partir do programa, caso em que decide se / quando / como ocorre, não você de fora.

O melhor que você pode fazer, até onde eu sei, é escrever um pouco de código que bifurca seu programa, aguarda e:

  • verifique o status de saída para detectar SIGXCPUe SIGXFSZ(AFAIK, esses sinais serão gerados apenas pelo SO para problemas de limite de recurso). Dependendo das suas necessidades exatas, você pode assumir isso SIGKILLe SIGSEGVtambém estar relacionado aos limites de recursos, mas isso é um pouco exagerado.
  • veja o que você pode obter getrusage(RUSAGE_CHILDREN,...)na sua implementação para obter uma dica sobre os outros.

Podem existir instalações específicas do sistema operacional para ajudar aqui (possivelmente coisas como ptraceLinux ou Solaris dtrace), ou possivelmente técnicas do tipo depurador, mas isso ficará ainda mais vinculado à sua implementação específica.


(Espero que outra pessoa responda com alguma coisa mágica que eu desconheço.)

Esteira
fonte
Está bem. Que tal apenas esses três: (Mem) exceder o limite de memória, (Time) limite de tempo, (Err) outro erro? Eu sei sobre fazer wrapper, mallocmas infelizmente não resolve o problema de memória em geral, porque em geral trata-se de chamada do sistema brk(estou certo?).
Grzegorz Wierzowiecki 26/10/11
1
O empacotamento do malloc não ajudará se você não controlar o programa. Se você está falando de hacks como LD_PRELOADing que do limite para o seu "não modificar o processo de" restrição, e vai ajudar um pouco, mas não muito - malloc, brk, sbrke mmapfalhará com ENOMEM, exatamente como se você realmente estivesse em uma situação de pouca memória (mas muito abaixo dos limites de memória). O limite de tempo é que RLIMIT_CPUeu não conheço um limite de relógio de parede.
Mat
Obrigado por me garantir brk. A meu ver, o requisito 'programa não está manipulando os sinais X, Y, Z ...' resolverá problemas de SIGXCPU, SIGXFSZ, SIGSEGV, graças ao waitpid (se estiver errado, por favor me corrija).
Grzegorz Wierzowiecki 29/10
1
O SIGSEGV pode ser criado em situações que não são violações dos limites de recursos (a desreferência de ponteiro nulo é a coisa mais comum que o gera) - você não pode ter certeza de que é um golpe final que o causa.
Mat
Obrigado por me garantir brk. A meu ver, o requisito 'programa não está manipulando os sinais X, Y, Z ...' resolverá problemas de SIGXCPU, SIGXFSZ, SIGSEGV, graças ao waitpid. Estou certo?
Grzegorz Wierzowiecki 29/10
3

Atualmente, estou trabalhando na mesma questão. Consegui ter uma solução parcial para isso. Eu usei o sistema de auditoria . Você pode acompanhar o trabalho em [1].

[1] https://github.com/PaulDaviesC/Logging-limits.conf

PaulDaviesC
fonte