What does signal.setitimer with signal.ITIMER_REAL do that signal.alarm cannot?
answer
- One call, three different clocks
- Floats instead of whole seconds
- A second argument makes it repeat
- Wall time versus CPU time
- Same underlying timer as the simpler call
basics
~10 ssignal.setitimer takes float seconds, so it can arm a sub-second deadline, and an optional interval argument that re-arms the timer for a repeating SIGALRM. signal.alarm is one-shot and whole-seconds. signal.getitimer reports what is left.
solid answer
~40 s`signal.setitimer(which, seconds, interval=0.0)` arms one of three per-process interval timers. With `signal.ITIMER_REAL` it counts wall-clock time and delivers `SIGALRM`, exactly like `signal.alarm` — but `seconds` is a float, so `0.25` is expressible, and a non-zero `interval` makes the timer re-arm itself after every delivery, giving a periodic tick instead of a one-shot deadline. Passing `0` as `seconds` disarms it, and `signal.getitimer(signal.ITIMER_REAL)` returns the `(delay, interval)` pair still pending. The other two flavours count CPU rather than wall time: `signal.ITIMER_VIRTUAL` counts the process's user-mode time and raises `SIGVTALRM`, `signal.ITIMER_PROF` counts user plus system time and raises `SIGPROF`. `ITIMER_REAL` and `signal.alarm` are the same underlying timer, so mixing them clobbers.
code
python · 10 linesimport signal, time
def on_alarm(signum, frame):
print("tick")
signal.signal(signal.SIGALRM, on_alarm)
signal.setitimer(signal.ITIMER_REAL, 0.2, 0.2) # first fire, then repeat
time.sleep(0.7)
signal.setitimer(signal.ITIMER_REAL, 0) # disarm
print("pending:", signal.getitimer(signal.ITIMER_REAL))go deeper
Recall the shape rather than the detail: there is a richer timer call than signal.alarm, it accepts fractional seconds, and a second argument makes it repeat. Knowing that signal.alarm rejects floats is enough to reach for it when you need one.
Explain the full signature, what disarming looks like, and the difference between the wall-clock and CPU-time flavours. Be able to say why the two spellings of the wall-clock timer collide and why the return value of setitimer matters.
Show the operational judgement around a repeating timer: keep the handler to a flag flip, know that deliveries collapse rather than queue, and be wary of installing a process-wide SIGALRM tick in code that other libraries share.
Own the choice of mechanism. A repeating signal perturbs the main thread by design, which is right for sampling and wrong for routine scheduling; decide when a plain timer thread is the safer default and where process-wide SIGALRM ownership is allowed to live.
`signal.alarm` is the minimal interface to a much richer Unix facility. `signal.setitimer` exposes the rest of it, and the three things it adds are fractional resolution, repetition, and a choice of clock. ### The signature ```python signal.setitimer(which, seconds, interval=0.0) ``` `which` selects one of three timers. `seconds` is a float giving the delay until the first delivery; passing `0` disarms the timer. `interval` is a float giving the gap between subsequent deliveries; leave it at `0.0` for a one-shot. The return value is the `(delay, interval)` pair that was previously armed, and `signal.getitimer(which)` reads the same pair without changing anything. Invalid values raise `signal.ItimerError`, a subclass of `OSError` — `signal.setitimer(signal.ITIMER_REAL, -1)` is the easy way to see it. ### The three timers **`signal.ITIMER_REAL`** counts wall-clock time and delivers `SIGALRM`. This is the one you want for a deadline: it does not care whether your process was running, sleeping, or descheduled. It is also, importantly, the *same* kernel timer that `signal.alarm` drives. Arming one cancels the other, and `signal.getitimer(signal.ITIMER_REAL)` will show the remaining time of an alarm set with `signal.alarm`. Treat them as two spellings of one resource. **`signal.ITIMER_VIRTUAL`** counts only the time the process spends executing in user mode, and delivers `SIGVTALRM`. A process blocked on I/O accumulates nothing. This is a profiling clock, not a deadline clock: it answers "how much CPU have I burned" rather than "how much time has passed". **`signal.ITIMER_PROF`** counts user plus system time and delivers `SIGPROF`. It is what sampling profilers historically used, since a tick every few milliseconds of CPU gives you a stack sample proportional to CPU consumption. Both of the CPU-based timers are per-process, and in a multi-threaded process the accounting sums across threads while the handler still runs only on the main thread. ### Fractional deadlines The practical reason to reach for `setitimer` in ordinary code is that `signal.alarm(0.5)` is a `TypeError` — it takes an integer count of seconds. A one-second floor is far too coarse for bounding, say, a single sensor read that normally completes in eight milliseconds. `signal.setitimer(signal.ITIMER_REAL, 0.05)` gives you the deadline you actually meant. Resolution beyond that is the kernel's business; the value is rounded to the timer's granularity, and delivery is a lower bound, not a promise — a busy machine can be late. ### Repetition, and the discipline it needs A non-zero `interval` turns the timer into a heartbeat. The classic uses are a watchdog that checks a liveness flag, a periodic flush, and a sampling profiler. Two rules keep it safe. First, **the handler must be tiny**. It runs in the main thread between bytecodes, so everything else in that thread is stalled while it runs, and a handler that takes longer than the interval means ticks arrive faster than you can consume them. The signal is not queued: several deliveries during one slow handler collapse into one. So set a flag, append to a preallocated list, or bump a counter — and do the real work in the normal control flow. Second, **disarm explicitly**. `signal.setitimer(signal.ITIMER_REAL, 0)` stops it. A repeating timer left armed keeps interrupting the process for its entire remaining life, which is a far noisier failure than a stale one-shot alarm. ### When to prefer something else A repeating `SIGALRM` is main-thread-only and Unix-only, and it perturbs whatever the main thread is doing. If all you need is "call this function every N seconds" and you are not trying to interrupt anything, a `threading.Timer` that re-arms itself, or a plain loop in a dedicated thread, is simpler, portable, and does not collide with any other user of `SIGALRM` in the process. Reach for `setitimer` when the point is precisely to interrupt the main thread — a deadline on a call that offers none, or a sampling tick that must land inside whatever code is running.
- How would you build a periodic watchdog tick with it, and what belongs in the handler?Arm `signal.setitimer(signal.ITIMER_REAL, 0.5, 0.5)` and keep the handler to a single cheap statement — set a flag or bump a counter. It runs in the main thread between bytecodes, so any real work there stalls the program, and ticks that arrive during a slow handler are collapsed rather than queued. Disarm with a `seconds` of 0 when you are done.
- What is the difference between signal.ITIMER_VIRTUAL and signal.ITIMER_REAL?ITIMER_REAL counts wall-clock time and delivers SIGALRM, so it fires even while the process is blocked on I/O — that is what you want for a deadline. ITIMER_VIRTUAL counts only user-mode CPU time and delivers SIGVTALRM, so a process waiting on a socket accumulates nothing. ITIMER_PROF sits between them, counting user plus system time and delivering SIGPROF.
- Can signal.alarm and signal.setitimer(signal.ITIMER_REAL, ...) be used together?Not independently — they drive the same per-process timer. Arming either one cancels whatever the other had pending, and signal.getitimer(signal.ITIMER_REAL) will report the remaining time of an alarm armed with signal.alarm. Pick one spelling for a codebase; mixing them across a library boundary is how a deadline quietly disappears.
saying these in an interview costs you the question
- Thinks signal.alarm accepts a float delay
- Believes setitimer and alarm are independent timers
- Expects a non-zero interval argument to fire only once
- Assumes ITIMER_VIRTUAL counts wall-clock time
- Does substantial work inside a repeating handler
- Forgets that a repeating timer must be disarmed explicitly