Kann launchd Programme häufiger als alle 10 Sekunden ausführen?

Ich habe einige Dienste wie diesen, die ich fast sofort ausführen möchte, nachdem Dateien geändert wurden.

<?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>

Selbst wenn ThrottleInterval auf 1 oder 0 gesetzt wurde, werden sie höchstens alle 10 Sekunden ausgeführt.

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.plistsagt nur, dass Programme standardmäßig nicht mehr als alle 10 Sekunden ausgeführt werden, erwähnt aber nicht, dass ThrottleInterval nicht darunter eingestellt werden konnte.

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.

Sie könnten das Programm oder Skript 10 Sekunden lang laufen lassen und jede Sekunde auf Änderungen achten:

#!/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

Oder das gleiche in 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

Aber gibt es eine Möglichkeit, das Zeitlimit zu umgehen oder zu verringern? Dies gilt auch für Ordneraktionen.

Antworten (3)

Es gibt keine Möglichkeit, das Zeitlimit zu umgehen oder zu verringern.

In der Apple-Dokumentation zum Erstellen von Launchd-Jobs heißt es:

Wichtig Wenn Ihr Daemon nach dem Start zu schnell heruntergefahren wird, denkt launchd möglicherweise, dass er abgestürzt ist. Daemons, die dieses Verhalten fortsetzen, werden möglicherweise angehalten und nicht erneut gestartet, wenn zukünftige Anforderungen eintreffen. Um dieses Verhalten zu vermeiden, fahren Sie mindestens 10 Sekunden nach dem Start nicht herunter.

Ihr Programm oder Skript muss mindestens 10 Sekunden lang ausgeführt werden. Erwägen Sie, eine Schleife zu implementieren, um in den letzten zehn Sekunden nach Dateiänderungsdaten zu suchen, zehn Sekunden lang zu schlafen und dies zu wiederholen.

Alternativ können Sie bestimmte Dateien mithilfe der kqueue- oder FSEvents-APIs überwachen. Diese StackOverflow-Frage kann hilfreich sein, Benachrichtigung über Dateisystemänderungen auf Dateiebene in Mac OS X.

Sie könnten Ihr Skript in einer Schleife laufen lassen, die nach geänderten Dateien sucht, anstatt es zu beenden, wenn es fertig ist. Lassen Sie es einige Sekunden schlafen, nachdem Sie nach den geänderten Dateien gesucht haben. Wenn es geänderte Dateien findet, fahren Sie mit dem Skript fort. Wenn nicht, schlafen Sie noch einmal.

Starten Sie dann Ihr Skript alle x Minuten mit launchd, nur für den Fall, dass die vorherige Ausführung stirbt. Codieren Sie den Anfang Ihres Skripts, um zu prüfen, ob bereits eine andere Instanz ausgeführt wird, und sich selbst zu beenden, falls dies der Fall ist.

launchd scheint keine weitere Instanz zu starten, wenn die vorherige noch läuft.
launchd startet nicht mehrere Instanzen desselben Jobtickets.

Wenn Sie ein Skript öfter als alle 10 Sekunden starten müssen, kann es in Bezug auf das "Forken" teuer werden (sprich: Speicher zuweisen, neue Prozesse starten usw.).

Daher schreibt man in diesem Fall am besten einen eigenen „ Daemon “ (Programm, was im Hintergrund läuft)

Ich empfehle Ihnen, eine "fähigere" Sprache als BASH zu verwenden (mein Favorit ist "Perl", aber Ruby ist auch in Ordnung), weil ein guter Daemon Timeouts, Alarme und so weiter behandelt - Dinge, die in reiner Bash zu schwer zu implementieren sind. (Natürlich kann der Daemon bei Bedarf auch Ihre Bash-Skripte ausführen). Die Grundlagen sind:

  • Skript, das endlos läuft und auf ein Ereignis wartet. Das Ereignis kann eine Netzwerkeingabe oder ein einfacher Timer oder etwas Ähnliches sein. Wenn das Ereignis eintrifft (z. B. der Wartezustand endete), wird das Skript tun, was Sie wollen, und der Zyklus wiederholt sich.

In der Perl-Welt gibt es bereits Module, die Ihr Skript als "Daemon"-Prozess abstimmen, zum Beispiel Proc::Daemon . Ich habe keine Erfahrung mit Ruby, aber dieser Artikel kann Ihnen helfen.

Sie können Ihren Daemon-Prozess über Launchd beim Systemstart oder über die Automator-App beim Anmelden oder manuell über das Terminal starten.