Comment vérifier quelle limite a été dépassée? (Le processus s'est terminé à cause d'ulimit.)

11

Supposons que le processus s'exécute dans un environnement illimité:

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

Le programme est terminé.

Il peut y avoir plusieurs raisons: mémoire / temps / limite de fichiers dépassée; tout simplement segfault; ou même une terminaison normale avec le code retour 0.

Comment vérifier quelle était la raison de l'arrêt du programme, sans modifier le programme?

PS Je veux dire "quand le binaire est donné". Peut-être qu'un wrapper (ptrace-ing, etc.) pourrait aider?

Grzegorz Wierzowiecki
la source

Réponses:

6

D'une manière générale, je ne pense pas que vous puissiez malheureusement. (Certains systèmes d'exploitation peuvent le prévoir, mais je ne connais pas ceux que je connais qui prennent cela en charge.)

Doc de référence pour les limites de ressources: à getrlimitpartir de POSIX 2008.

Prenons par exemple la limite CPU RLIMIT_CPU.

  • Si le processus dépasse la limite souple, il reçoit un message SIGXCPU
  • Si le processus dépasse la limite stricte, il obtient un simple SIGKILL

Si vous le pouvez wait()sur votre programme, vous pouvez savoir s'il a été tué par SIGXCPU. Mais vous ne pouviez pas différencier un SIGKILLenvoyé pour violation de la limite stricte d'un ancien meurtre ordinaire de l'extérieur. De plus, si le programme gère le XCPU, vous ne le verrez même pas de l'extérieur.

Même chose pour RLIMIT_FSIZE. Vous pouvez voir le à SIGXFSZpartir de l' wait()état si le programme ne le gère pas. Mais une fois la limite de taille de fichier dépassée, la seule chose qui se produit est que les E / S supplémentaires qui tentent de tester à nouveau cette limite recevront simplement EFBIG- cela sera géré (ou non, malheureusement) par le programme en interne. Si le programme gère SIGXFSZ, comme ci-dessus - vous ne le saurez pas.

RLIMIT_NOFILE? Eh bien, vous ne recevez même pas de signal. openet les amis reviennent EMFILEau programme. Ce n'est pas gêné autrement, donc il échouera (ou non) de la manière dont il a été codé pour échouer dans cette situation.

RLIMIT_STACK? Bon vieux SIGSEGV, ne peut pas être distingué de la vingtaine d'autres raisons de se faire livrer. (Vous saurez que c'est ce qui a tué le processus, d'après le waitstatut.)

RLIMIT_ASet RLIMIT_DATAva juste faire malloc()et quelques autres commencent à échouer (ou à recevoir SIGSEGVsi la limite AS est atteinte en essayant d'étendre la pile sous Linux). À moins que le programme ne soit très bien écrit, il échouera probablement de manière assez aléatoire à ce stade.

Donc, en bref, généralement, les échecs ne sont pas visiblement différents des autres raisons de mort du processus, vous ne pouvez donc pas être sûr ou peuvent être entièrement gérés à partir du programme, auquel cas il décide si / quand / comment cela se déroule, pas vous de l'exterieur.

Pour autant que je sache, le mieux que vous puissiez faire est d'écrire un peu de code qui bifurque votre programme, attend dessus et:

  • vérifier l'état de sortie à détecter SIGXCPUet SIGXFSZ(AFAIK, ces signaux ne seront générés par le système d'exploitation que pour les problèmes de limite de ressources). En fonction de vos besoins exacts, vous pouvez supposer cela SIGKILLet SIGSEGVétaient également liés aux limites de ressources, mais c'est un peu exagéré.
  • regardez ce que vous pouvez retirer de getrusage(RUSAGE_CHILDREN,...)votre implémentation pour obtenir un indice sur les autres.

Des fonctionnalités spécifiques au système d'exploitation peuvent exister pour vous aider ici (peut-être des choses comme ptracesous Linux ou Solaris dtrace), ou éventuellement des techniques de type débogueur, mais cela va être encore plus lié à votre implémentation spécifique.


(J'espère que quelqu'un d'autre répondra avec quelque chose de magique que je ne connais pas du tout.)

Tapis
la source
D'accord. Qu'en est-il seulement de ces trois: (Mem) dépassement de la limite de mémoire, (Time) limite de temps, (Err) autre erreur? Je sais comment faire du wrapper, mallocmais malheureusement cela ne résout pas le problème de mémoire en général, car en général il s'agit d'un appel système brk(ai-je raison?).
Grzegorz Wierzowiecki
1
Envelopper malloc n'aidera pas si vous ne contrôlez pas le programme. Si vous parlez hacks comme LD_PRELOADING , qui de limite pour votre contrainte « ne pas modifier le processus », et il aide un peu, mais pas vraiment - malloc, brk, sbrket mmapéchouera avec ENOMEM, exactement comme si vous étiez vraiment dans une situation de mémoire (mais bien en dessous des limites de la mémoire). Le délai est RLIMIT_CPU, je ne connais pas de limite d'horloge murale.
Mat
Merci de m'avoir assuré brk. Comme je le vois, l'exigence `` le programme ne gère pas les signaux X, Y, Z ... '' résoudra les problèmes de SIGXCPU, SIGXFSZ, SIGSEGV, grâce à waitpid (si je me trompe, corrigez-moi s'il vous plaît).
Grzegorz Wierzowiecki
1
SIGSEGV peut être déclenché dans des situations qui ne sont pas des violations de limite de ressources (la déréférence de pointeur nul étant la chose la plus courante qui le déclenche) - vous ne pouvez pas être sûr que c'est un hit ulimit qui le provoque.
mat
Merci de m'avoir assuré brk. Comme je le vois, l'exigence «le programme ne gère pas les signaux X, Y, Z ...» résoudra les problèmes de SIGXCPU, SIGXFSZ, SIGSEGV, grâce à waitpid. Ai-je raison?
Grzegorz Wierzowiecki
3

Je travaille actuellement sur la même question. J'ai pu y trouver une solution partielle. J'ai utilisé un sous-système d' audit . Vous pouvez suivre le travail à [1].

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

PaulDaviesC
la source