I would like to have a (bash) script executed once or twice every second in the background. I see two possible solutions:

  1. Include a loop in the script (with a sleep command) and execute the script once.
  2. Include no loop and have the script executed repeatedly via a systemd timer.

So my questions are:

  • What are possible drawbacks and advantages to these approaches?
  • Are there better alternatives?

“Once or twice every second” is frequent enough that I would go for the loop option, but let’s go through some of the differences.

  • A script with a loop consumes a small amount of resources (occupies one PID, code loaded into memory [though possibly swapped out], etc.) even when it’s not doing anything. The cost of a systemd timer is lower, basically one timerfd I believe.

    • In the case of a shell script (as opposed to e. g. a Python script), you’re also paying a small cost for launching new sleep processes all the time. (Unless sleep is a builtin in your shell, but in Bash it isn’t.)
  • A timer pays the cost of starting the program every time. This cost varies by program (for a Java program, for instance, it would be substantial, throwing away optimization results every time the program ends; with a shell script it’s probably less severe).

  • A timer will cause its corresponding service to log to the journal every time it’s started and stopped. (This, to me, would be the main reason to not choose a timer in your scenario. Several messages per second is a lot of logspam.)

  • Starting a new service instance each time the timer elapses gives you separate resource accounting per instance (see systemd.resource-control(5)). If e. g. for some reason your service consumed much more disk I/O at one in the morning, this would be recorded in the journal for later analysis, assuming you enable the accounting (it’s part of the log message: “Consumed x CPU time, read y from disk”, etc.).

  • systemd timers by default have an accuracy of only one minute, to allow multiple timers to be started at the same time, reducing CPU wakeups. This can be changed (e. g. AccuracySec=1us, see systemd.timer(5)), but it may be a sign that firing a timer on a per-second basis is outside the most typical use case.

  • A systemd timer lets you conveniently see when it last elapsed, and when it’s next scheduled to elapse again, using systemctl list-timers. A home-grown sleep loop probably doesn’t offer the same level of insight.

  • With a timer, changes to the service become effective the next time the timer fires. If the service has its own loop, it needs to be restarted to pick up changes.

I don’t know much of other alternatives. The venerable cron (in whatever implementation you use) likely has some of the same benefits as systemd timers (e. g. lower dormant cost), as well as some of the same drawbacks (e. g. higher startup cost).

  • Great answer! Thanks a lot. I'll go with the loop. – Nos Apr 21 '20 at 8:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.