pause on ARM
I was tracing some syscalls to check when and how the sleep
command was being executed.
I was expecting to see a call to clock_nanosleep
, but instead I found a call to ppoll(NULL, 0, NULL, NULL, 0)
, so I got curious.
I filtered the running processes and I’ve found sleep inf
running, so I’ve discovered that sleep
supports the “inf” argument to sleep indefinitely.
This was not documented in the man page, so I looked at sleep source code to know more,
but in the code there were no references to the “inf” syntax, so I got even more curious.
I realized the sleep uses strtod
to parse its arguments which,
according to its man page,
parses the “INF” or “INFINITY” string as the IEEE 754 special value for positive infinite (which is 0x7ff0000000000000
, btw).
This value is passed to an internal helper called xnanosleep
, which detects the infinite value and calls pause()
to sleep forever.
This is a very smart wat to sleep indefinitely, instead of using a loop to call sleep
in a cycle.
But why ppoll
was being called instead of pause
? Obviously, the result is the same, but why don’t just call pause
directly?
So I wrote this small tool to see what syscall was effectively being called:
#include <unistd.h>
int main(void)
{
pause();
return 0;
}
The answer is: depends on the architecture. This happens on an ARM machine:
$ strace -e clock_nanosleep,ppoll,pause ./pause
ppoll(NULL, 0, NULL, NULL, 281473054674784
and this on an x86 machine:
$ strace -e clock_nanosleep,ppoll,pause ./pause
pause(
A quick look at the kernel syscall source
shows that pause
is available on architectures that define __ARCH_WANT_SYS_PAUSE
, which is set for i386 and arm but not arm64.
So, the ppoll
call is a fallback for architectures that don’t have the pause
syscall, like arm64.