Launchd peut-il exécuter des programmes plus fréquemment que toutes les 10 secondes?

8

J'ai certains services comme celui-ci que j'aimerais exécuter presque immédiatement après la modification des fichiers.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>test</string>
    <key>ProgramArguments</key>
    <array>     
        <string>say</string>
        <string>a</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/Users/username/Desktop/</string>
    </array>
</dict>
</plist>

Même si ThrottleInterval a été défini sur 1 ou 0, ils ne s'exécutent qu'au maximum toutes les 10 secondes.

9/9/12 4:57:05.457 PM com.apple.launchd.peruser.501[138]: (test) Throttling respawn: Will start in 7 seconds
9/9/12 4:57:09.541 PM com.apple.launchd.peruser.501[138]: (test) Throttling respawn: Will start in 3 seconds

man launchd.plist indique seulement que les programmes ne sont pas exécutés plus de toutes les 10 secondes par défaut, mais ne mentionne pas que ThrottleInterval ne peut pas être défini en dessous.

ThrottleInterval <integer>
This key lets one override the default throttling policy imposed on jobs by launchd.
The value is in seconds, and by default, jobs will not be spawned more than once
every 10 seconds.  The principle behind this is that jobs should linger around just
in case they are needed again in the near future. This not only reduces the latency
of responses, but it encourages developers to amortize the cost of program invoca-
tion.

Vous pouvez garder le programme ou le script en cours d'exécution pendant 10 secondes et surveiller les modifications chaque seconde:

#!/bin/bash

start=$(date +%s)
prev=

until (( $(date +%s) >= $start + 10 )); do
    new=$(stat -f %m ~/Desktop/)
    [[ $prev != $new ]] && say a
    prev=$new
    sleep 1
done

Ou la même chose en Ruby:

#!/usr/bin/env ruby

start = Time.now
prev = nil

until Time.now >= start + 10
  current = File.mtime("#{ENV['HOME']}/Desktop/")
  `say a` if current != prev
  prev = current
  sleep 1
end

Mais existe-t-il un moyen de contourner ou de réduire le délai? Cela s'applique également aux actions de dossier.

Lri
la source

Réponses:

9

Il n'y a aucun moyen de contourner ou de diminuer la limite de temps.

La documentation d'Apple concernant la création de jobs Launchd indique ce qui suit:

Important Si votre démon s'arrête trop rapidement après son lancement, launchd peut penser qu'il s'est bloqué. Les démons qui continuent ce comportement peuvent être suspendus et ne pas être relancés lorsque de futures demandes arriveront. Pour éviter ce problème, n'arrêtez pas pendant au moins 10 secondes après le lancement.

Votre programme ou script doit continuer à fonctionner pendant au moins 10 secondes. Envisagez d'implémenter une boucle pour vérifier les dates de modification des fichiers au cours des dix dernières secondes, dormir pendant dix secondes et répéter.

Alternativement, vous pouvez regarder des fichiers spécifiques en utilisant les API kqueue ou FSEvents. Cette question StackOverflow peut être utile, le changement de système de fichiers au niveau des fichiers notification sous Mac OS X .

Graham Miln
la source
2

Vous pouvez continuer à exécuter votre script dans une boucle en vérifiant les fichiers modifiés au lieu de quitter lorsqu'il est terminé. Faites-le dormir pendant quelques secondes après avoir vérifié les fichiers modifiés. S'il trouve des fichiers modifiés, continuez avec le script. Sinon, dormez à nouveau.

Demandez ensuite à launchd de démarrer votre script toutes les x minutes au cas où l'exécution précédente se termine. Codez le début de votre script pour vérifier si une autre instance est déjà en cours d'exécution et si c'est le cas, quittez-la.

Logiciel insomniaque
la source
launchd ne semble pas démarrer une autre instance si la précédente est toujours en cours d'exécution.
Lri
launchd ne lancera pas plusieurs instances du même ticket de travail.
Graham Miln
1

Si vous avez besoin de démarrer un script plus souvent que toutes les 10 secondes, il peut être coûteux en termes de "forking" (lire: allouer de la mémoire, démarrer de nouveaux processus, etc.).

Par conséquent, dans ce cas, il est préférable d'écrire votre propre " démon " (programme, ce qui s'exécute en arrière-plan)

Je vous recommande d'utiliser un langage "plus performant" comme BASH (mon préféré est "perl", mais ruby ​​est OK aussi) car un bon démon gère les délais d'attente, les alarmes et ainsi de suite - des choses qui sont trop difficiles à implémenter en pure bash. (Bien sûr, le démon peut également exécuter vos scripts bash - si nécessaire). Les bases sont:

  • script ce qui tourne sans fin et attend un événement. L'événement peut être une entrée réseau, une simple minuterie ou quelque chose du genre. Lorsque l'événement arrive (par exemple, l'état d'attente terminé), le script fait ce que vous voulez et le cycle se répète.

Dans le monde Perl, il existe déjà des modules qui ajustent votre script en tant que processus "démon", par exemple Proc :: Daemon . Je n'ai pas d'expérience avec le rubis, mais cet article peut vous aider.

Vous pouvez démarrer votre processus démon via Launchd au démarrage du système, ou depuis l'application automate lorsque vous vous connectez, ou depuis le terminal manuellement.

jm666
la source