There are many sleep functions in C language, right? Often I don't know where to use what. That said, you can't be a true C programmer if you choose the sleep function properly. C programmers have a mission to choose the best of many options and deliver unrivaled performance.
In this article, I will write about the difference between usleep, nanosleep, and clock_nanosleep, which can realize highly accurate sleep processing.
The target Linux kernel version is ** 5.4.2 **.
After this section, I will read the source code of the Linux kernel, but I do not know that, so for those who want to give a conclusion quickly, I will write the conclusion first.
How, these three functions do *** almost the same behavior ***!
Only clock_nanosleep can change its behavior depending on the argument, but if you pass CLOCK_MONOTONIC as the first argument of clock_nanosleep, the behavior of the three functions will be the same.
However, each of these functions has different usability. It seems best to use a function that is easy to call in each situation. If you want to make sure that they match at the source code level, see the following sections.
Let's see what kind of functions usleep, nanosleep, and clock_nanosleep are. This information is taken from man's Linux Programmer's Manual.
usleep usleep --Defer execution in microseconds
#include <unistd.h>
int usleep(useconds_t usec);
nanosleep nanosleep-high precision sleep
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
clock_nanosleep clock_nanosleep --Precise stop (sleep) at the specified clock
#include <time.h>
int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *request,
struct timespec *remain);
Well, what I want to do is almost the same. I want to sleep at the nanosecond level.
Next, I would like to classify each function by system call or library function. (Well, system calls are also provided by the library side to wrap the assembler, but here we classify them according to whether they are defined as system calls in the Linux kernel or not.)
usleep | nanosleep | clock_nanosleep |
---|---|---|
Library function | System call | System call |
It looks like this.
Now let's look at each function at the source code level.
usleep
Now let's look at the definition of usleep.
usleep is a function defined by glibc and is not a Linux system call. You can then expect usleep to be calling a system call to do sleep in that function. When I actually looked into the source code of glibc, it was as follows.
https://github.com/lattera/glibc/blob/master/sysdeps/posix/usleep.c
int
usleep (useconds_t useconds)
{
struct timespec ts = { .tv_sec = (long int) (useconds / 1000000),
.tv_nsec = (long int) (useconds % 1000000) * 1000ul };
/* Note the usleep() is a cancellation point. But since we call
nanosleep() which itself is a cancellation point we do not have
to do anything here. */
return __nanosleep (&ts, NULL);
}
Looking at this source code and comments, usleep seems to be calling the nanosleep system call. In other words *** glibc "Isn't it troublesome to create a timespec structure for the argument of nanosleep? I defined a wrapper function" *** It is defined as such. Therefore, it can be said that usleep is a function that receives microseconds, creates a timespec structure based on it, and finally calls the nanosleep system call. For now, I know that usleep and nanosleep will behave similarly.
nanosleep Since nanosleep is a Linux system call, we will read the source code of the Linux kernel. nanosleep is defined as a system call /kernel/time/hrtimer.c line:1945 https://elixir.bootlin.com/linux/v5.4.2/source/kernel/time/hrtimer.c#L1945 is.
SYSCALL_DEFINE2(nanosleep, struct __kernel_timespec __user *, rqtp,
struct __kernel_timespec __user *, rmtp)
{
struct timespec64 tu;
if (get_timespec64(&tu, rqtp))
return -EFAULT;
if (!timespec64_valid(&tu))
return -EINVAL;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp;
return hrtimer_nanosleep(&tu, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
}
You can finally see that we are calling a function called hrtimer_nanosleep. This function is a function that performs sleep processing using a high-precision timer called High Resolution Timer. CLOCK_MONOTONIC is passed as the third argument of the hrtimer_nanosleep function. Oh, this should have been a constant that could also be passed to clock_nanosleep. Now that I've smelled something, let's take a look at the implementation of the clock_nanosleep system call.
clock_nanosleep Finally, read the source code of clock_nanosleep. This system call /kernel/time/posix-stubs.c line: 124 https://elixir.bootlin.com/linux/v5.4.2/source/kernel/time/posix-stubs.c#L124 It is defined in.
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
const struct __kernel_timespec __user *, rqtp,
struct __kernel_timespec __user *, rmtp)
{
struct timespec64 t;
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
break;
default:
return -EINVAL;
}
if (get_timespec64(&t, rqtp))
return -EFAULT;
if (!timespec64_valid(&t))
return -EINVAL;
if (flags & TIMER_ABSTIME)
rmtp = NULL;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp;
return hrtimer_nanosleep(&t, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
which_clock);
}
This function first looks at the specified clockid. Other than CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME, it seems that an error will occur. After reading, ***, except for the flags check, it does almost the same as the definition of the nanosleep system call we saw in the previous section. *** ***
For the time being, the description is different from nanosleep
flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL
I will explain this part briefly as well. flags are flags that can be passed to the clock_nanosleep system call. If *** TIMER_ABSTIME *** is set for this flag, HRTIMER_MODE_ABS will be passed to the *** hrtimer_nanosleep *** function if it is not set. According to man's description, if you specify the *** TIMER_ABSTIME *** flag,
** If flags is TIMER_ABSTIME, the remain argument is not used and is not needed (stopping at an absolute value can be called again with the same request argument). ** **
is what they said. In other words, I found that this process was added to handle the clock_nanosleep flag.
I've said the conclusion above, but apparently these three functions behave in much the same way. In other words, each method ultimately calls the hrtimer_nanosleep function, and the process of determining the arguments to pass to that function is different. If I force you to make a difference, I think it's that flexibility. usleep and nanosleep can only use CLOCK_MONOTONIC as a sleep flag, but clock_nanosleep can be freely decided by the programmer.
The conclusion was that there wasn't much difference in the end, but an open source culture was essential to reach this conclusion. The best open source, I hope that free software will continue to develop!
Recommended Posts