如何睡几微秒

发布于 2024-10-17 05:54:55 字数 1234 浏览 1 评论 0 原文

考虑以下代码:

#include <stdio.h>
#include <time.h>
#include <math.h>

// Compile with gcc -lrt -lm -o test_clock test_clock.c

#define CLOCK CLOCK_MONOTONIC

int main(int argc, char** argv) {
    double temp, elapsed;
    int j;
    struct timespec requestStart, requestEnd, req;

    // Pseudo-sleep
    clock_gettime(CLOCK, &requestStart);
    temp = 0;
    for(j=0; j < 40; j++)
        temp += sin(j);
    clock_gettime(CLOCK, &requestEnd);
    elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
                 + ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;
    printf("Elapsed: %lf us\n", elapsed);

    // Nanosleep
    clock_gettime(CLOCK, &requestStart);
    req.tv_nsec = 5000;
    req.tv_sec = 0;
    clock_nanosleep(CLOCK, 0, &req, NULL);
    clock_gettime(CLOCK, &requestEnd);
    elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
                 + ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;

    printf("Elapsed: %lf us\n", elapsed);

}

在我的 2.6.32 系统上,结果是

Elapsed: 5.308000 us
Elapsed: 69.142000 us

我同意这很可能是因为 nanosleep() 要求内核重新安排进程。我怎样才能避免这种情况?我想保留 CPU 的所有权并闲置一段精确的时间。

Consider the following code:

#include <stdio.h>
#include <time.h>
#include <math.h>

// Compile with gcc -lrt -lm -o test_clock test_clock.c

#define CLOCK CLOCK_MONOTONIC

int main(int argc, char** argv) {
    double temp, elapsed;
    int j;
    struct timespec requestStart, requestEnd, req;

    // Pseudo-sleep
    clock_gettime(CLOCK, &requestStart);
    temp = 0;
    for(j=0; j < 40; j++)
        temp += sin(j);
    clock_gettime(CLOCK, &requestEnd);
    elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
                 + ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;
    printf("Elapsed: %lf us\n", elapsed);

    // Nanosleep
    clock_gettime(CLOCK, &requestStart);
    req.tv_nsec = 5000;
    req.tv_sec = 0;
    clock_nanosleep(CLOCK, 0, &req, NULL);
    clock_gettime(CLOCK, &requestEnd);
    elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
                 + ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;

    printf("Elapsed: %lf us\n", elapsed);

}

On my 2.6.32 system, the result is

Elapsed: 5.308000 us
Elapsed: 69.142000 us

I agree that this is most likely because nanosleep() asks the kernel to reschedule the process. How can I avoid this? I want to keep ownership of the CPU and just idle around for a precise amount of time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

如日中天 2024-10-24 05:54:55

如果您希望您的应用程序能够尽可能精确地“睡眠”,请首先将您的应用程序置于实时条件下,

  • 为您的程序/线程使用实时调度程序类:SCHED_FIFOSCHED_RR
  • 提升程序/线程优先级
  • ,如果您要“睡眠”的时间少于内核要处理的最小数量,

请手动 busywait 查看 http://www.drdobbs.com/184402031

还有另一个问题:nanosleep CPU 使用率高?

If you want your application to be able to "sleep" as precisely as possible, first put your application in realtime conditions

  • use a realtime scheduler class for your program / thread : SCHED_FIFO or SCHED_RR
  • elevate your program / thread priority
  • and if you're going to "sleep" for less than the minimum amount the kernel is going to handle, manually busywait

Have a look at http://www.drdobbs.com/184402031

And this other question: nanosleep high cpu usage?

混吃等死 2024-10-24 05:54:55

操作系统调度程序不会执行类似“哦,将此线程从处理器上移出正好 86 个时钟周期,然后将其重新打开”之类的操作。

你放弃了处理器,你就放弃了处理器。当您需要时,操作系统会让您重新启动。很可能您必须等到其他正在运行的程序放弃处理器才能重新启动。

The OS scheduler is not going to do anything like "oh, take this thread off the processor for exactly 86 clock cycles then put it back on".

You give up the processor, you've given up the processor. The OS will put you back on when it feels like it. Chances are you'll have to wait until whatever else is running gives up the processor before you can sneak back on.

北城孤痞 2024-10-24 05:54:55

如何配置 Linux SCHED_RR 软实时循环调度程序,以便将 clock_nanosleep() 的睡眠分辨率从约 55 秒降低到最低约 4 秒us 最小值,具体取决于您的硬件

问题摘要

OP 正确使用了 clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_time, NULL) 的等效项来尝试休眠请求的时钟时间恰好为 5000 ns,或 5 us。请参阅: https://man7.org/linux/man-pages/ man2/clock_nanosleep.2.html 和问题。 然而,尽管被命令睡眠5 秒,但实际睡眠时间为69 秒。为什么?

我遇到了同样类型的问题。我命令 clock_nanosleep() 休眠 1 ns0.001 us),它平均休眠 55 us 。这似乎是可能的最小睡眠时间间隔。为什么?我们可以做得更好吗?

答案摘要

  1. 我凭经验证实,在运行默认 Linux 调度程序的情况下,我的 x86-64 Linux Ubuntu 系统上可能的最小 nanosleep 时间间隔为 ~55000 ns(~55 us) 。您命令的任何小于该时间的睡眠时间都将睡眠该时间。 这是在使用 Linux 默认常规 SCHED_OTHER/SCHED_NORMAL“默认 Linux 分时”调度程序时出现的情况。
    1. 在此处了解各种 Linux 调度程序类型:https:// man7.org/linux/man-pages/man7/sched.7.html
  2. 您可以将其改进为~4000 ns(~4 us)的最小睡眠时间,只需使用SCHED_RR循环软实时调度程序(推荐) 、SCHED_FIFO先进/先出软实时调度程序。

这是我的 sleep_nanosleep_minimum_time_interval.c 的测试结果 程序:

  1. SCHED_OTHER/SCHED_NORMAL “默认 Linux 分时”调度程序:
    1. 可能的最短纳睡眠时间:~55000 ns(~55 us)
  2. SCHED_RR 循环软实时调度程序,最低优先级为 1
    1. 可能的最短纳睡眠时间:~4000 ns(~4 us)

  3. SCHED_RR 循环软实时调度程序,最高优先级为 99
    1. 可能的最短纳睡眠时间:~4000 ns(~4 us)(同上)

  4. SCHED_FIFO 先进/先出软实时调度程序 1 的最低优先级:
    1. 可能的最短纳睡眠时间:~4000 ns(~4 us)(同上)
  5. SCHED_FIFO 先进/先出软实时调度程序 99 的最高优先级
    1. 可能的最短纳睡眠时间:~4000 ns(~4 us)(同上)

如您所见,您会立即获得巨大的延迟时间(55000/4000 = 13.75x) )只需从 SCHED_OTHER/SCHED_NORMAL 非实时 Linux 调度程序切换到软实时 Linux 调度程序即可立即得到改进,例如 SCHED_FIFOSCHED_RR,具有任意优先级。根据我的测试,这两个调度程序中的哪一个以及您选择哪个优先级并不那么重要。您必须知道您想要哪个线程获得什么优先级以及何时获得,以便调整您自己的线程。作为初学者,设置具有非常短的睡眠时间的线程,通常 1~10 ms:

  1. SCHED_RR 软实时循环调度程序,以及:
  2. 更高优先级。

作为初学者,请对所有实时线程使用最低优先级 1SCHED_RR,然后根据需要调整他们的优先级。您不想想立即选择一个非常高的优先级并不必要地阻止高优先级任务。仅根据需要提高线程或进程的优先级。

如何设置 Linux“策略”(调度程序)和“优先级”

@Yann Droneaud 的回答 很有帮助,但它没有显示如何使用实时调度程序。然而,它链接到的文章解释了更多内容(但有一些错误和疏忽),Ask Ubuntu Q&A 也是如此。我研究了这两个来源以了解下面提供的选项:

  1. 博士。 Dobb 的:使用 Linux 进行软实时编程
  2. 询问 Ubuntu:如何使用 SCHED_RR 运行程序命令行中的策略?

以下是如何设置 Linux 调度程序“策略”(调度程序)和“优先级”:

  1. 选项 1(最简单):从命令行,使用chrt“更改实时”命令调用您的程序:
    # 使用 `chrt` 调用您的程序以在调用时指定调度程序!
    
    # 通用格式:
    sudo chrt [--scheduler_policy] <优先级> <命令>
    # 或者
    sudo chrt [--scheduler_policy] -p <优先级> 
    
    # 上面第一种形式的示例: 
    
    # 使用 SCHED_RR 以最低优先级 1 调用 `my_prog`
    sudo chrt --rr 1 my_prog
    # 使用 SCHED_RR 调用 `my_prog`,最高优先级为 99
    须藤 chrt --rr 99 my_prog
    # 使用 SCHED_FIFO 以最低优先级 1 调用 `my_prog`
    须藤 chrt --fifo 1 my_prog
    # 使用 SCHED_FIFO 调用 `my_prog`,最高优先级为 99
    须藤 chrt --fifo 99 my_prog
    

    要查看给定调度程序可用的优先级值,请运行chrt --max。如果在具有 chrt BusyBox 实现的嵌入式 Linux 系统上,请改用 chrt -m。以下是我的 x86-64 Linux Ubuntu 机器上的运行和输出:

    $ chrt --max
    SCHED_OTHER 最小/最大优先级:0/0
    SCHED_FIFO 最小/最大优先级:1/99
    SCHED_RR 最小/最大优先级:1/99
    SCHED_BATCH 最小/最大优先级:0/0
    SCHED_IDLE 最小/最大优先级:0/0
    SCHED_DEADLINE 最小/最大优先级:0/0
    

    如您所见,对于 SCHED_FIFOSCHED_RR1/99 显示最低优先级为 1,并且最高优先级是99

  2. 选项 2:在您的代码中,将运行的进程 ID (PID) 设置为所需的调度程序策略优先级我提供了 5 个完整的演示与这一切有关。请参阅我的 sleep_nanosleep_minimum_time_interval.c 测试文件:
    int retcode; // 返回代码以检查函数调用中的错误
    
    // ------------------------------------------------ ------------------------
    // 演示1:使用 `sched_setscheduler()` 改变当前进程的
    // 调度程序“策略”**和**“优先级”。
    // 看:
    // 1. https://man7.org/linux/man-pages/man2/sched_setscheduler.2.html
    // 1. 所有 `errno` 错误:https://man7.org/linux/man-pages/man3/errno.3.html
    // 1. `mlockall()`: https://man7.org/linux/man-pages/man2/mlock.2.html
    // 1. *****https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    // ------------------------------------------------ ------------------------
    {
        const struct sched_pa​​rampriority_param =
        {
            // 优先级必须从 1(最低优先级)到 99
            // (最高优先级)用于`SCHED_FIFO`和`SCHED_RR`
            //(循环)调度策略;看:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = 1,
        };
    
        // 注意:使用`0`作为`pid`(第一个参数)来指示这个PID
        // 运行进程
        retcode = sched_setscheduler(0, SCHED_RR, &priority_param);
        if (重新编码 == -1)
        {
            printf("错误:在文件 %s: %i 中:无法设置调度程序。"
                   "错误号 = %i: %s。\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
        别的
        {
            printf("`sched_setscheduler()`成功。\n");
        }
    
        // 内存锁:同时将内存锁定到 RAM 中,这样内核就不会被锁定
        // 允许将其移动到交换空间,否则这将是一个
        // 运行速度慢并且打破了这个的“实时”特性
        // 过程。
        // 看:
        // 1. https://man7.org/linux/man-pages/man2/mlock.2.html
        // 1. 本教程/博客文章:
        // https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
        retcode = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
        if (重新编码 == -1)
        {
            printf("错误:在文件 %s 中:%i:无法将内存锁定到 RAM 中。"
                   "错误号 = %i: %s。\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
        别的
        {
            printf("`mlockall()`成功。\n");
        }
    } // 演示 1 结束
    
    // ------------------------------------------------ ------------------------
    // 演示 2:使用 `sched_setparam()` 来更改 **仅** 的“优先级”
    // 正在运行的进程。
    // 看:
    // 1. https://man7.org/linux/man-pages/man2/sched_setparam.2.html
    // 1. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    // 1. “清单 1”演示代码:该代码展示了如何抚养孩子
    // 优先级,降低子优先级,并提高优先级以便
    // 获得一个互斥锁,否则它将永远无法获得
    // 获取是否有更高优先级的进程拥有它:
    // https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=2
    // ------------------------------------------------ ------------------------
    {
        常量 int new_priority = 2;
        const struct sched_pa​​rampriority_param =
        {
            // 优先级必须从 1(最低优先级)到 99
            // (最高优先级)用于`SCHED_FIFO`和`SCHED_RR`
            //(循环)调度策略;看:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = new_priority,
        };
        // 注意:使用`0`作为`pid`(第一个参数)来指示这个PID
        // 运行进程
        retcode = sched_setparam(0, &priority_param);
        if (重新编码 == -1)
        {
            printf("错误:在文件 %s 中:%i:无法设置优先级。"
                   "错误号 = %i: %s。\n",
                __FILE__, __LINE__, errno, strerror(errno));
            // 注意:通过测试,`errno` 似乎被设置为 22
            // (EINVAL),如果不使用 `sudo` 来运行此代码。看来
            // 就像一个编译器错误,因为它应该是 `EPERM`,但是让我们
            // 就像处理`EPERM`一样处理它。
            if (errno == EPERM || errno == EINVAL) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
        别的
        {
            printf("`sched_setparam()`成功。\n");
        }
    } // 演示 2 结束
    
    // ------------------------------------------------ ------------------------
    // [这是我首选的通用技术] <==================
    // 演示 3(演示 1 的 pthread 版本):如果使用 pthreads:使用
    // `pthread_setschedparam()` 改变当前线程的调度程序
    //“策略”和“优先级”。
    // 看:
    // 1. https://man7.org/linux/man-pages/man3/pthread_setschedparam.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_self.3.html
    // 1. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    // 1.https://askubuntu.com/a/1129915/327339
    // ------------------------------------------------ ------------------------
    {
        pthread_t this_thread = pthread_self();
        const struct sched_pa​​rampriority_param =
        {
            // 优先级必须从 1(最低优先级)到 99
            // (最高优先级)用于`SCHED_FIFO`和`SCHED_RR`
            //(循环)调度策略;看:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = 1,
        };
        retcode = pthread_setschedparam(this_thread, SCHED_RR, &priority_param);
        if (重新编码!= 0)
        {
            printf("错误:在文件 %s 中:%i:无法设置 pthread 调度程序。"
                   "重新编码 = %i: %s。\n",
                    __FILE__、__LINE__、重编码、strerror(重编码));
            if (retcode == EPERM) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
        别的
        {
            printf("`pthread_setschedparam()`成功。\n");
        }
    
        // 内存锁:同样将内存锁定到RAM中以防止操作缓慢
        // 内核将其放入交换空间的位置。请参阅上面的注释。
        retcode = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
        if (重新编码 == -1)
        {
            printf("错误:在文件 %s 中:%i:无法将内存锁定到 RAM 中。"
                   "错误号 = %i: %s。\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
        别的
        {
            printf("`mlockall()`成功。\n");
        }
    } // 演示 3 结束
    
    // ------------------------------------------------ ------------------------
    // 演示 4(演示 2 的 pthread 版本):如果使用 pthreads:使用
    // `pthread_setschedprio()` 仅更改当前线程的“优先级”。
    // 看:
    // 1. https://man7.org/linux/man-pages/man3/pthread_setschedprio.3.html
    // ------------------------------------------------ ------------------------
    {
        pthread_t this_thread = pthread_self();
    
        // 优先级必须从 1(最低优先级)到 99
        // (最高优先级)用于“SCHED_FIFO”和“SCHED_RR”(循环)
        // 调度策略;看:
        // https://man7.org/linux/man-pages/man7/sched.7.html
        常量 int 优先级 = 3;
        retcode = pthread_setschedprio(this_thread, 优先级);
        if (重新编码!= 0)
        {
            printf("错误:在文件 %s 中:%i:无法设置 pthread 优先级。"
                   "重新编码 = %i: %s。\n",
                    __FILE__、__LINE__、重编码、strerror(重编码));
            // NB:通过测试,似乎`pthread_setschedprio
            // 如果不使用“sudo”运行此代码,则 ()` 返回 22(EINVAL)。
            // 这看起来像是一个编译器错误,因为它应该是 `EPERM`,
            // 但让我们像处理 `EPERM` 一样处理它。
            if (retcode == EPERM || retcode == EINVAL) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
        别的
        {
            printf("`pthread_setschedprio()`成功。\n");
        }
    } // 演示 4 结束
    
    // ------------------------------------------------ ------------------------
    // 演示 5(使用所需的调度程序 **策略** 创建一个 pthread
    // 和创建时的**优先级**):如果使用 pthreads:使用
    // `pthread_attr_setschedpolicy()` 和 `pthread_attr_setschedparam()`
    // 设置初始调度程序**策略**和**优先级**
    // 通过 `pthread_create()` 创建线程。不要忘记使用
    // `pthread_attr_setinheritsched()` 强制 `pthread_create()` 使用我们的
    // 新设置而不是从调用方继承调度程序设置
    // 线!您应该使用`pthread_attr_init()`和`pthread_attr_destroy()`
    // 以及初始化和销毁​​属性对象。
    // 看:
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_init.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_setschedpolicy.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_setschedparam.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_setinheritsched.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_create.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_join.3.html
    // 1. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    // 1. “清单 2”代码演示了以下部分代码:
    // https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=3
    // ------------------------------------------------ ------------------------
    {
        // 0.内存锁:同样将内存锁定到RAM中以防止操作缓慢
        // 内核将其放入交换空间的位置。请参阅上面的注释。
        retcode = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
        if (重新编码 == -1)
        {
            printf("错误:在文件 %s 中:%i:无法将内存锁定到 RAM 中。"
                   "错误号 = %i: %s。\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
        别的
        {
            printf("`mlockall()`成功。\n");
        }
    
        // 1. 创建并初始化pthread属性对象。
    
        pthread_attr_t pthread_attr;
        retcode = pthread_attr_init(&pthread_attr);
        if (重新编码!= 0)
        {
            printf("错误:`pthread_attr_init()`失败。"
                   "重新编码 = %i: %s。\n",
                   重编码,strerror(重编码));
        }
    
        // 2.为下一个线程设置调度器**策略**(调度器类型)
        // 待创建。
    
        // 设置为 RR(循环)软实时调度程序。
        int Scheduler_policy = SCHED_RR;
        retcode = pthread_attr_setschedpolicy(&pthread_attr,scheduler_policy);
        if (重新编码!= 0)
        {
            printf("错误:`pthread_attr_setschedpolicy()`失败。"
                   "重新编码 = %i: %s。\n",
                   重编码,strerror(重编码));
        }
    
        // 3. 为下一个要创建的线程设置调度程序**优先级**。
    
        const struct sched_pa​​rampriority_param =
        {
            // 优先级必须从 1(最低优先级)到 99
            // (最高优先级)用于`SCHED_FIFO`和`SCHED_RR`
            //(循环)调度策略;看:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = 1,
        };
        retcode = pthread_attr_setschedparam(&pthread_attr, &priority_param);
        if (重新编码!= 0)
        {
            printf("错误:`pthread_attr_setschedparam()`失败。"
                   "重新编码 = %i: %s。\n",
                   重编码,strerror(重编码));
        }
    
        // 4. 设置调度程序继承属性,以便`pthread_create()`
        // 将使用上面`pthread_attr`中设置的调度程序设置
        // 对象而不是从调用者继承调度程序属性
        // 线!如果您不调用此函数,则默认行为是
        // `pthread_create()` 忽略你的调度策略和优先级
        // 设置`pthread_attr`对象内部,并使用调用
        // 线程调度策略和优先级相反!
        retcode = pthread_attr_setinheritsched(&pthread_attr,
            PTHREAD_EXPLICIT_SCHED);
        if (重新编码!= 0)
        {
            printf("错误:`pthread_attr_setinheritsched()`失败。"
                   "重新编码 = %i: %s。\n",
                   重编码,strerror(重编码));
        }
    
        // 5. 使用此创建任意数量的新 pthread(POSIX 线程)线程
        // 在线程创建时设置调度程序策略和优先级。这是
        // 仅创建一个 pthread 的演示。
    
        pthread_t new_thread;
        retcode = pthread_create(&new_thread, &pthread_attr,
            dummy_pthread_action,“new_thread”);
        if (重新编码!= 0)
        {
            printf("错误:`pthread_create()`失败。"
                   "重新编码 = %i: %s。\n",
                   重编码,strerror(重编码));
            if (retcode == EPERM) // 错误:权限
            {
                printf(" 您必须使用 `sudo` 或以 root 身份运行此程序"
                       “拥有适当的权限!\n”);
            }
        }
    
        // 6. 销毁线程属性对象。完成后使用
        // 上面创建任意数量的`pthread_attr_t`属性对象
        // 你想要的pthreads,销毁它,大概是为了释放动态
        // 内存并防止内存泄漏。
    
        retcode = pthread_attr_destroy(&pthread_attr);
        if (重新编码!= 0)
        {
            printf("错误:`pthread_attr_destroy()`失败。"
                   "重新编码 = %i: %s。\n",
                   重编码,strerror(重编码));
        }
    
        // 7. 线程清理:等待 `new_thread` 完成其任务
        // 通过加入它来等待然后清理它的任务。
        // 参见:https://man7.org/linux/man-pages/man3/pthread_join.3.html
        const char* return_message;
        retcode = pthread_join(new_thread, (void**)&return_message);
        if (重新编码!= 0)
        {
            printf("错误:`pthread_join()`失败。"
                   "重新编码 = %i: %s。\n",
                   重编码,strerror(重编码));
        }
        别的
        {
            printf("`pthread_join()`成功:return_message = \"%s\"\n",
                返回消息);
        }
    } // 演示 5 结束
    

另请参阅

  1. 我的其他文件:
    1. sleep_nanosleep.c
    2. sleep_nanosleep_minimum_time_interval.c
    3. timinglib_sleep_and_sleep_until.c
    4. timinglib.h
    5. timinglib.c
    6. timinglib_pthread_periodic_loop.c
      1. 我展示了如何使用 SCHED_RR 实时循环调度程序和打开绝对计时标志的 clock_nanosleep() 来执行 10 kHz 固定周期循环。
  2. [我的答案] pthread_create 无法与 pthread_attr_setschedparam 正常工作
  3. [我的答案] 如何从

nanosleep() 输出 创建 sleep_us() /ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/c/sleep_nanosleep_minimum_time_interval.c" rel="nofollow noreferrer">sleep_nanosleep_minimum_time_interval.cSCHED_RR 循环实时时间调度程序:

请注意,错误平均约为 4 us(向右滚动一点):

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c17 sleep_nanosleep_minimum_time_interval.c timinglib.c -o bin/a -lm -pthread && time sudo bin/a
Attempt to sleep 1 ns per `clock_nanosleep()` call, 100000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 1
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =             4124 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =             2720 ns;   error =        -3 **us**
maximum time for a `clock_nanosleep()` sleep call        =            32544 ns;   error =       -33 **us**

Attempt to sleep 100 ns per `clock_nanosleep()` call, 100000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 100
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =             4456 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =             2682 ns;   error =        -3 **us**
maximum time for a `clock_nanosleep()` sleep call        =            29349 ns;   error =       -29 **us**

Attempt to sleep 1000 ns per `clock_nanosleep()` call, 100000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 1000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =             4096 ns;   error =        -3 **us**
minimum time for a `clock_nanosleep()` sleep call        =             2693 ns;   error =        -2 **us**
maximum time for a `clock_nanosleep()` sleep call        =            37962 ns;   error =       -37 **us**

Attempt to sleep 10000 ns per `clock_nanosleep()` call, 1000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 10000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =            13583 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =            11991 ns;   error =        -2 **us**
maximum time for a `clock_nanosleep()` sleep call        =            29361 ns;   error =       -19 **us**

Attempt to sleep 100000 ns per `clock_nanosleep()` call, 1000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 100000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =           103944 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =           102299 ns;   error =        -2 **us**
maximum time for a `clock_nanosleep()` sleep call        =           121937 ns;   error =       -22 **us**

Attempt to sleep 1000000 ns per `clock_nanosleep()` call, 1000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 1000000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =          1005035 ns;   error =        -5 **us**
minimum time for a `clock_nanosleep()` sleep call        =          1002823 ns;   error =        -3 **us**
maximum time for a `clock_nanosleep()` sleep call        =          1108260 ns;   error =      -108 **us**

Attempt to sleep 1000000003 ns per `clock_nanosleep()` call, 2 times.
ts_requested.tv_sec  = 1
ts_requested.tv_nsec = 3
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =       1000008524 ns;   error =        -9 **us**
minimum time for a `clock_nanosleep()` sleep call        =       1000007190 ns;   error =        -7 **us**
maximum time for a `clock_nanosleep()` sleep call        =       1000009859 ns;   error =       -10 **us**

Attempt to sleep 100000000 ns per `clock_nanosleep()` call, 10 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 100000000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =        100008022 ns;   error =        -8 **us**
minimum time for a `clock_nanosleep()` sleep call        =        100007113 ns;   error =        -7 **us**
maximum time for a `clock_nanosleep()` sleep call        =        100008965 ns;   error =        -9 **us**

Total program run time = 5.404382936 sec.

real    0m5.447s
user    0m0.105s
sys 0m0.691s

How to configure the Linux SCHED_RR soft real-time round-robin scheduler so that clock_nanosleep() can have improved sleep resolution as low as ~4 us minimum, down from ~55 us minimum, depending on your hardware

Summary of the question

The OP correctly used the equivlanet of clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_time, NULL) to try to sleep a requested time of exactly 5000 ns, or 5 us. See: https://man7.org/linux/man-pages/man2/clock_nanosleep.2.html and the question. However, the actual time slept was 69 us despite being commanded to sleep 5 us. Why?

I ran into this same type of issue. I was commanding clock_nanosleep() to sleep 1 ns (0.001 us), and it slept on average 55 us. That seems to be the smallest sleep time interval possible. Why? Can we make this any better?

Answer summary

  1. I have empirically confirmed that the smallest nanosleep time interval possible on my x86-64 Linux Ubuntu system is ~55000 ns (~55 us) with the default Linux scheduler running. Any sleep time you command which is less than that amount of time will sleep that amount of time. This is when using Linux's default regular SCHED_OTHER/SCHED_NORMAL "Default Linux time-sharing" scheduler, however.
    1. Read about the various Linux scheduler types here: https://man7.org/linux/man-pages/man7/sched.7.html.
  2. You can improve this to a minimum sleep time of ~4000 ns (~4 us) instead, simply by using the SCHED_RR round-robin soft real-time scheduler (recommended), or the SCHED_FIFO first-in/first-out soft real-time scheduler.

Here are my test results from my sleep_nanosleep_minimum_time_interval.c program:

  1. SCHED_OTHER/SCHED_NORMAL "Default Linux time-sharing" scheduler:
    1. Minimum nanosleep time possible: ~55000 ns (~55 us)
  2. SCHED_RR round-robin soft real-time scheduler with the lowest priority of 1:
    1. Minimum nanosleep time possible: ~4000 ns (~4 us)
  3. SCHED_RR round-robin soft real-time scheduler with the highest priority of 99:
    1. Minimum nanosleep time possible: ~4000 ns (~4 us) (same as above)
  4. SCHED_FIFO first-in/first-out soft real-time scheduler with the lowest priority of 1:
    1. Minimum nanosleep time possible: ~4000 ns (~4 us) (same as above)
  5. SCHED_FIFO first-in/first-out soft real-time scheduler with the highest priority of 99:
    1. Minimum nanosleep time possible: ~4000 ns (~4 us) (same as above)

As you can see, you get an immediate and huge (55000/4000 = 13.75x) improvement immediately just by switching from the SCHED_OTHER/SCHED_NORMAL non-realtime Linux scheduler to a soft realtime Linux scheduler, such as SCHED_FIFO or SCHED_RR, with any priority level. Which of those two schedulers, and which priority you choose, based on my testing, is less important. You'd have to know which thread you want to get what priority, and when, in order to tweak your own threads. As a starter, set threads which have really short sleep times, generally < 1~10 ms to:

  1. The SCHED_RR soft real-time round-robin scheduler, and to:
  2. Higher priorities.

As a starter, use SCHED_RR with the lowest priority of 1 for all your real-time threads, and then adjust their priorities up from there, as needed. You do not want to choose a really high priority right off the bat and unnecessarily block high-priority tasks. Only raise the priority of a thread or process as-needed.

How to set the Linux "policy" (scheduler) and "priority"

This answer by @Yann Droneaud is helpful, but it doesn't show how to use a real-time scheduler. The article it links to, however, explains more (but is has a few errors and oversights), as does this Ask Ubuntu Q&A. I studied these two sources to learn the options I present below:

  1. Dr. Dobb's: Soft Real-Time Programming with Linux
  2. Ask Ubuntu: How to run a program with SCHED_RR policy from command line?

Here is how to set your Linux scheduler "policy" (scheduler) and "priority":

  1. Option 1 (easiest): from the command-line, call your program with the chrt "change real-time" command:
    # Call your program with `chrt` to specify the scheduler at call-time!
    
    # General format:
    sudo chrt [--scheduler_policy] <priority> <command>
    # OR
    sudo chrt [--scheduler_policy] -p <priority> <pid>
    
    # Examples of the first form above: 
    
    # call `my_prog` with SCHED_RR with lowest priority of 1
    sudo chrt --rr 1 my_prog
    # call `my_prog` with SCHED_RR with highest priority of 99
    sudo chrt --rr 99 my_prog
    # call `my_prog` with SCHED_FIFO with lowest priority of 1
    sudo chrt --fifo 1 my_prog
    # call `my_prog` with SCHED_FIFO with highest priority of 99
    sudo chrt --fifo 99 my_prog
    

    To see what priority values are available for a given scheduler, run chrt --max. If on an embedded Linux system with the BusyBox implementation of chrt, use chrt -m instead. Here is the run and output on my x86-64 Linux Ubuntu machine:

    $ chrt --max
    SCHED_OTHER min/max priority    : 0/0
    SCHED_FIFO min/max priority : 1/99
    SCHED_RR min/max priority   : 1/99
    SCHED_BATCH min/max priority    : 0/0
    SCHED_IDLE min/max priority : 0/0
    SCHED_DEADLINE min/max priority : 0/0
    

    As you can see, for both SCHED_FIFO and SCHED_RR, 1/99 shows that the lowest priority is 1 and the highest priority is 99.

  2. Option 2: in your code, set your running process ID (PID) to a desired scheduler policy and priority. I have provided 5 thorough demos related to all this. See "Demo 1" through "Demo 5" inside function set_scheduler() in my sleep_nanosleep_minimum_time_interval.c test file:
    int retcode; // return code to check for errors from function calls
    
    // -------------------------------------------------------------------------
    // Demo 1: use `sched_setscheduler()` to change the current process's
    // scheduler "policy" **and** "priority".
    // See:
    // 1. https://man7.org/linux/man-pages/man2/sched_setscheduler.2.html
    // 1. All `errno` errors: https://man7.org/linux/man-pages/man3/errno.3.html
    // 1. `mlockall()`: https://man7.org/linux/man-pages/man2/mlock.2.html
    // 1. *****https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    // -------------------------------------------------------------------------
    {
        const struct sched_param priority_param =
        {
            // the priority must be from 1 (lowest priority) to 99
            // (highest priority) for the `SCHED_FIFO` AND `SCHED_RR`
            // (round robin) scheduler policies; see:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = 1,
        };
    
        // Note: use `0` as the `pid` (1st param) to indicate the PID of this
        // running process
        retcode = sched_setscheduler(0, SCHED_RR, &priority_param);
        if (retcode == -1)
        {
            printf("ERROR: in file %s: %i: Failed to set scheduler. "
                   "errno = %i: %s.\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
        else
        {
            printf("`sched_setscheduler()` successful.\n");
        }
    
        // Memory lock: also lock the memory into RAM so that the kernel is NOT
        // allowed to move it into the swap space, which would otherwise be a
        // slow operation and break the "real-time" characteristics of this
        // process.
        // See:
        // 1. https://man7.org/linux/man-pages/man2/mlock.2.html
        // 1. This tutorial/blog post:
        //    https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
        retcode = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
        if (retcode == -1)
        {
            printf("ERROR: in file %s: %i: Failed to lock memory into RAM. "
                   "errno = %i: %s.\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
        else
        {
            printf("`mlockall()` successful.\n");
        }
    } // end of Demo 1
    
    // -------------------------------------------------------------------------
    // Demo 2: use `sched_setparam()` to change **only** the "priority" of the
    // running process.
    // See:
    // 1. https://man7.org/linux/man-pages/man2/sched_setparam.2.html
    // 1. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    //      1. "Listing 1" demo code: this code shows how to raise a child
    //      priority, lower a child priority, and raise a priority in order to
    //      obtain a mutex lock which otherwise it would never be able to
    //      obtain if a higher-priority process has it:
    //      https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=2
    // -------------------------------------------------------------------------
    {
        const int new_priority = 2;
        const struct sched_param priority_param =
        {
            // the priority must be from 1 (lowest priority) to 99
            // (highest priority) for the `SCHED_FIFO` AND `SCHED_RR`
            // (round robin) scheduler policies; see:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = new_priority,
        };
        // Note: use `0` as the `pid` (1st param) to indicate the PID of this
        // running process
        retcode = sched_setparam(0, &priority_param);
        if (retcode == -1)
        {
            printf("ERROR: in file %s: %i: Failed to set priority. "
                   "errno = %i: %s.\n",
                __FILE__, __LINE__, errno, strerror(errno));
            // NB: through testing, it seems that `errno` gets set to 22
            // (EINVAL), if `sudo` is not used to run this code. That seems
            // like a compiler bug, because it should be `EPERM`, but let's
            // just handle it as though it was `EPERM`.
            if (errno == EPERM || errno == EINVAL)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
        else
        {
            printf("`sched_setparam()` successful.\n");
        }
    } // end of Demo 2
    
    // -------------------------------------------------------------------------
    // [THIS IS MY PREFERRED TECHNIQUE FOR GENERAL USE]  <==================
    // Demo 3 (the pthread version of Demo 1): if using pthreads: use
    // `pthread_setschedparam()` to change the current thread's scheduler
    // "policy" and "priority".
    // See:
    // 1. https://man7.org/linux/man-pages/man3/pthread_setschedparam.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_self.3.html
    // 1. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    // 1. https://askubuntu.com/a/1129915/327339
    // -------------------------------------------------------------------------
    {
        pthread_t this_thread = pthread_self();
        const struct sched_param priority_param =
        {
            // the priority must be from 1 (lowest priority) to 99
            // (highest priority) for the `SCHED_FIFO` AND `SCHED_RR`
            // (round robin) scheduler policies; see:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = 1,
        };
        retcode = pthread_setschedparam(this_thread, SCHED_RR, &priority_param);
        if (retcode != 0)
        {
            printf("ERROR: in file %s: %i: Failed to set pthread scheduler. "
                   "retcode = %i: %s.\n",
                    __FILE__, __LINE__, retcode, strerror(retcode));
            if (retcode == EPERM)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
        else
        {
            printf("`pthread_setschedparam()` successful.\n");
        }
    
        // Memory lock: also lock the memory into RAM to prevent slow operations
        // where the kernel puts it into swap space. See notes above.
        retcode = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
        if (retcode == -1)
        {
            printf("ERROR: in file %s: %i: Failed to lock memory into RAM. "
                   "errno = %i: %s.\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
        else
        {
            printf("`mlockall()` successful.\n");
        }
    } // end of Demo 3
    
    // -------------------------------------------------------------------------
    // Demo 4 (the pthread version of Demo 2): if using pthreads: use
    // `pthread_setschedprio()` to change only the current thread's "priority".
    // See:
    // 1. https://man7.org/linux/man-pages/man3/pthread_setschedprio.3.html
    // -------------------------------------------------------------------------
    {
        pthread_t this_thread = pthread_self();
    
        // the priority must be from 1 (lowest priority) to 99
        // (highest priority) for the `SCHED_FIFO` AND `SCHED_RR`(round robin)
        // scheduler policies; see:
        // https://man7.org/linux/man-pages/man7/sched.7.html
        const int priority = 3;
        retcode = pthread_setschedprio(this_thread, priority);
        if (retcode != 0)
        {
            printf("ERROR: in file %s: %i: Failed to set pthread priority. "
                   "retcode = %i: %s.\n",
                    __FILE__, __LINE__, retcode, strerror(retcode));
            // NB: through testing, it seems that `pthread_setschedprio
            // ()` returns 22(EINVAL), if `sudo` is not used to run this code.
            // That seems like a compiler bug, because it should be `EPERM`,
            // but let's just handle it as though it was `EPERM`.
            if (retcode == EPERM || retcode == EINVAL)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
        else
        {
            printf("`pthread_setschedprio()` successful.\n");
        }
    } // end of Demo 4
    
    // -------------------------------------------------------------------------
    // Demo 5 (create a pthread with the desired scheduler **policy**
    // and **priority** at creation time): if using pthreads: use
    // `pthread_attr_setschedpolicy()` and `pthread_attr_setschedparam()` to
    // set an initial scheduler **policy** and **priority** at the time of
    // thread creation via `pthread_create()`. Don't forget to use
    // `pthread_attr_setinheritsched()` to force `pthread_create()` to use our
    // new settings instead of inheriting scheduler settings from the calling
    // thread! You should use `pthread_attr_init()` and `pthread_attr_destroy()`
    // as well to initialize and destroy the attributes object.
    // See:
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_init.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_setschedpolicy.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_setschedparam.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_attr_setinheritsched.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_create.3.html
    // 1. https://man7.org/linux/man-pages/man3/pthread_join.3.html
    // 1. https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=1
    //      1. "Listing 2" code which demonstrates some of this code below:
    //         https://www.drdobbs.com/soft-real-time-programming-with-linux/184402031?pgno=3
    // -------------------------------------------------------------------------
    {
        // 0. Memory lock: also lock the memory into RAM to prevent slow operations
        // where the kernel puts it into swap space. See notes above.
        retcode = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
        if (retcode == -1)
        {
            printf("ERROR: in file %s: %i: Failed to lock memory into RAM. "
                   "errno = %i: %s.\n",
                __FILE__, __LINE__, errno, strerror(errno));
            if (errno == EPERM)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
        else
        {
            printf("`mlockall()` successful.\n");
        }
    
        // 1. Create and initialize a pthread attribute object.
    
        pthread_attr_t pthread_attr;
        retcode = pthread_attr_init(&pthread_attr);
        if (retcode != 0)
        {
            printf("ERROR: `pthread_attr_init()` failed. "
                   "retcode = %i: %s.\n",
                   retcode, strerror(retcode));
        }
    
        // 2. Set the scheduler **policy** (scheduler type) for the next thread
        // to be created.
    
        // Set to RR (round robin) soft real-time scheduler.
        int scheduler_policy = SCHED_RR;
        retcode = pthread_attr_setschedpolicy(&pthread_attr, scheduler_policy);
        if (retcode != 0)
        {
            printf("ERROR: `pthread_attr_setschedpolicy()` failed. "
                   "retcode = %i: %s.\n",
                   retcode, strerror(retcode));
        }
    
        // 3. Set the scheduler **priority** for the next thread to be created.
    
        const struct sched_param priority_param =
        {
            // the priority must be from 1 (lowest priority) to 99
            // (highest priority) for the `SCHED_FIFO` AND `SCHED_RR`
            // (round robin) scheduler policies; see:
            // https://man7.org/linux/man-pages/man7/sched.7.html
            .sched_priority = 1,
        };
        retcode = pthread_attr_setschedparam(&pthread_attr, &priority_param);
        if (retcode != 0)
        {
            printf("ERROR: `pthread_attr_setschedparam()` failed. "
                   "retcode = %i: %s.\n",
                   retcode, strerror(retcode));
        }
    
        // 4. Set the scheduler inheritance attribute so that `pthread_create()`
        // will use the scheduler settings set above inside the `pthread_attr`
        // object rather than inheriting scheduler attributes from the calling
        // thread! If you don't call this function, the default behavior is for
        // `pthread_create()` to IGNORE your scheduler policy and priority
        // settings inside the `pthread_attr` object, and use the calling
        // threads scheduler policy and priority instead!
        retcode = pthread_attr_setinheritsched(&pthread_attr,
            PTHREAD_EXPLICIT_SCHED);
        if (retcode != 0)
        {
            printf("ERROR: `pthread_attr_setinheritsched()` failed. "
                   "retcode = %i: %s.\n",
                   retcode, strerror(retcode));
        }
    
        // 5. Create any number of new pthread (POSIX thread) threads with this
        // scheduler policy and priority set at thread creation time. Here is
        // a demo creating just one pthread.
    
        pthread_t new_thread;
        retcode = pthread_create(&new_thread, &pthread_attr,
            dummy_pthread_action, "new_thread");
        if (retcode != 0)
        {
            printf("ERROR: `pthread_create()` failed. "
                   "retcode = %i: %s.\n",
                   retcode, strerror(retcode));
            if (retcode == EPERM)  // Error: Permissions
            {
                printf("  You must use `sudo` or run this program as root to "
                       "have proper privileges!\n");
            }
        }
    
        // 6. Destroy the thread attribute object. When done using the
        // `pthread_attr_t` attribute object above to create any number of
        // pthreads you desire, destroy it, presumably to free up dynamic
        // memory and prevent memory leaks.
    
        retcode = pthread_attr_destroy(&pthread_attr);
        if (retcode != 0)
        {
            printf("ERROR: `pthread_attr_destroy()` failed. "
                   "retcode = %i: %s.\n",
                   retcode, strerror(retcode));
        }
    
        // 7. thread cleanup: wait for the `new_thread` to finish with its
        // task by joining with it to wait and then clean it up.
        // See: https://man7.org/linux/man-pages/man3/pthread_join.3.html
        const char* return_message;
        retcode = pthread_join(new_thread, (void**)&return_message);
        if (retcode != 0)
        {
            printf("ERROR: `pthread_join()` failed. "
                   "retcode = %i: %s.\n",
                   retcode, strerror(retcode));
        }
        else
        {
            printf("`pthread_join()` successful: return_message = \"%s\"\n",
                return_message);
        }
    } // end of Demo 5
    

See also

  1. my other files:
    1. sleep_nanosleep.c
    2. sleep_nanosleep_minimum_time_interval.c
    3. timinglib_sleep_and_sleep_until.c
    4. timinglib.h
    5. timinglib.c
    6. timinglib_pthread_periodic_loop.c
      1. I show how to do a 10 kHz fixed-period loop using SCHED_RR real-time round-robin scheduler, and clock_nanosleep() with the absolute timing flag on.
  2. [my answer] pthread_create not working properly with pthread_attr_setschedparam
  3. [my answer] How to create sleep_us() from nanosleep()

Output from sleep_nanosleep_minimum_time_interval.c with SCHED_RR round-robin real-time scheduler:

Notice that the error averages around 4 us (scroll to the right a little):

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c17 sleep_nanosleep_minimum_time_interval.c timinglib.c -o bin/a -lm -pthread && time sudo bin/a
Attempt to sleep 1 ns per `clock_nanosleep()` call, 100000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 1
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =             4124 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =             2720 ns;   error =        -3 **us**
maximum time for a `clock_nanosleep()` sleep call        =            32544 ns;   error =       -33 **us**

Attempt to sleep 100 ns per `clock_nanosleep()` call, 100000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 100
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =             4456 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =             2682 ns;   error =        -3 **us**
maximum time for a `clock_nanosleep()` sleep call        =            29349 ns;   error =       -29 **us**

Attempt to sleep 1000 ns per `clock_nanosleep()` call, 100000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 1000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =             4096 ns;   error =        -3 **us**
minimum time for a `clock_nanosleep()` sleep call        =             2693 ns;   error =        -2 **us**
maximum time for a `clock_nanosleep()` sleep call        =            37962 ns;   error =       -37 **us**

Attempt to sleep 10000 ns per `clock_nanosleep()` call, 1000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 10000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =            13583 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =            11991 ns;   error =        -2 **us**
maximum time for a `clock_nanosleep()` sleep call        =            29361 ns;   error =       -19 **us**

Attempt to sleep 100000 ns per `clock_nanosleep()` call, 1000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 100000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =           103944 ns;   error =        -4 **us**
minimum time for a `clock_nanosleep()` sleep call        =           102299 ns;   error =        -2 **us**
maximum time for a `clock_nanosleep()` sleep call        =           121937 ns;   error =       -22 **us**

Attempt to sleep 1000000 ns per `clock_nanosleep()` call, 1000 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 1000000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =          1005035 ns;   error =        -5 **us**
minimum time for a `clock_nanosleep()` sleep call        =          1002823 ns;   error =        -3 **us**
maximum time for a `clock_nanosleep()` sleep call        =          1108260 ns;   error =      -108 **us**

Attempt to sleep 1000000003 ns per `clock_nanosleep()` call, 2 times.
ts_requested.tv_sec  = 1
ts_requested.tv_nsec = 3
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =       1000008524 ns;   error =        -9 **us**
minimum time for a `clock_nanosleep()` sleep call        =       1000007190 ns;   error =        -7 **us**
maximum time for a `clock_nanosleep()` sleep call        =       1000009859 ns;   error =       -10 **us**

Attempt to sleep 100000000 ns per `clock_nanosleep()` call, 10 times.
ts_requested.tv_sec  = 0
ts_requested.tv_nsec = 100000000
failure_cnt                                              =                0
average time required per `clock_nanosleep()` sleep call =        100008022 ns;   error =        -8 **us**
minimum time for a `clock_nanosleep()` sleep call        =        100007113 ns;   error =        -7 **us**
maximum time for a `clock_nanosleep()` sleep call        =        100008965 ns;   error =        -9 **us**

Total program run time = 5.404382936 sec.

real    0m5.447s
user    0m0.105s
sys 0m0.691s
舞袖。长 2024-10-24 05:54:55

好吧,您必须学会忍受它,因为手册页部分指出:由于系统延迟和硬件计时器分辨率可能存在的限制,实际睡眠时间可能会更长 :-)

现在,关于您问题的答案,我最好的猜测是,这是因为您的第一个循环正在进程内运行。换句话说,不涉及上下文切换,因为您正在全力运行 CPU,并且您将在调度程序给您的 100 毫秒量子内完成所有工作。

然而,由于您明确要求进入睡眠状态,nanosleep 很有可能会将您切换出去。将进程置于紧密的 while 循环中直到持续时间结束,效率不会那么低下:-)

这意味着您会受到调度程序的所有变幻莫测的影响,包括 事实上,另一个进程可能会完全用完它的量子,因此您的进程可能会离开那里至少 100 毫秒。在负载足够重的系统上,它可能会出现相当长的一段时间。

Well, you'll have to learn to live with it since the man page states, in part: the actual time slept may be longer, due to system latencies and possible limitations in the timer resolution of the hardware :-)

Now as to the answer to your question, my best guess is that it's because your first loop is running in-process. In other words, there are no context switches involved since you're running the CPU flat out and you will be doing all that work within your 100ms quanta given to you by the scheduler.

However, there's a good chance that nanosleep will switch you out since you are explicitly asking to be put to sleep. It won't be so inefficient as to just put your process in a tight while loop until the duration is over :-)

That means you're subject to all the vagaries of the scheduler including the fact that another process may totally use up its quanta, hence your process may be out of there for 100ms at least. On a heavily-enough loaded system, it could be out for quite a while.

心的憧憬 2024-10-24 05:54:55
// busy wait for 10 microseconds
struct timespec ttime,curtime;

// get the time
clock_gettime(CLOCK_REALTIME,&ttime);

// clear the nanoseconds and keep the seconds in order not to overflow the nanoseconds
ttime.tv_nsec = 0;

// set it back
clock_settime(CLOCK_REALTIME,&ttime);

// get the time again 
clock_gettime(CLOCK_REALTIME,&ttime);

// increase the nano seconds by 10*1000
ttime.tv_nsec += 10000;

// loop
while(true){
  clock_gettime(CLOCK_REALTIME,&curtime);
  if (curtime.tv_nsec > ttime.tv_nsec)
    break;
}

// 它比 usleep 好得多。

// busy wait for 10 microseconds
struct timespec ttime,curtime;

// get the time
clock_gettime(CLOCK_REALTIME,&ttime);

// clear the nanoseconds and keep the seconds in order not to overflow the nanoseconds
ttime.tv_nsec = 0;

// set it back
clock_settime(CLOCK_REALTIME,&ttime);

// get the time again 
clock_gettime(CLOCK_REALTIME,&ttime);

// increase the nano seconds by 10*1000
ttime.tv_nsec += 10000;

// loop
while(true){
  clock_gettime(CLOCK_REALTIME,&curtime);
  if (curtime.tv_nsec > ttime.tv_nsec)
    break;
}

// it is much better than the usleep.

鸠书 2024-10-24 05:54:55

您可以使用 usleep 方法以微秒为单位获得睡眠。

you can use usleep method to get sleep in microsecond units.

楠木可依 2024-10-24 05:54:55

效率——一个允许以几个时钟周期的精度切换任务的操作系统几乎没有其他作用。

有专门的操作系统可以执行此操作 - 但在常规硬件上,您需要为虚拟机管理程序付出大量开销

Efficiency - an Os that allowed tasks to be switched in and out with a precision of a few clock cycles would do very little else.

There are specialized OSes that do this - but on regular hardware you pay a lot of overhead for the hypervisor

二智少女猫性小仙女 2024-10-24 05:54:55

这是一个固定的答案 - 我不知道相关的 linux 内部结构,希望专家能够来解决它。

一种可能性是,69us 只是取消调度然后重新调度线程的原始开销。即使睡眠时间很短,内核也可能会做很多工作来执行上下文切换(或者半个上下文切换,如果没有什么可安排的),然后几乎立即撤消它。我不知道在典型 PC 上的 Linux 上“应该”需要多长时间。

如果这不能解释它,调度程序通常有一个“时间片”的概念,这是调度程序考虑切换它之前调度线程将运行的时间,除非它自行调度或其他具有更高的调度线程。优先级变得可调度。内核将有低级计时器在时间片结束时触发中断(除了为某些其他事件(例如可以解锁线程的 I/O)触发的中断)。当时间片结束时,调度程序可以决定是继续使用同一线程,还是切换到另一个线程。

所以看起来好像当你睡觉时,要么(a)调度程序实际上并没有设置一个计时器来让你的线程在请求​​的时间进行调度,它只是在等待一个时间片,所以CPU的空闲时间比必要的时间要长;否则(b)它使您的线程可以在请求的时间进行调度,但是当您通过睡眠放弃执行时,其他具有相同优先级的线程进入,并且调度程序没有理由优先选择您,直到“轮到您了” ” 再次根据调度程序通常使用的任何规则来决定调度哪个线程。

不过,69us 对于时间切片来说还是相当短的。

您似乎有一个基本的解决方案 - 您可以通过坐在循环中检查时间来延迟很短的时间,就像自旋锁一样。然而,正如其他人所说,在非实时系统中,或多或少根据定义,您不能要求调度程序在任何特定时间运行您的线程。即使在实时系统中,如果您与同等优先级的线程竞争,您也可能会失败,而如果您与更高优先级的线程竞争,您将失败。

This is a holding answer - I don't know the relevant linux internals, hopefully an expert can come along and clear it up.

One possibility is that that 69us is simply the raw overhead of descheduling and then rescheduling the thread. Even though the sleep is short, the kernel might do a lot of work to perform a context switch (or half a context switch, if there's nothing to schedule), and then undo it almost immediately. I don't know how long that "should" take on linux on a typical PC.

If that doesn't explain it, a scheduler generally has a concept of a "timeslice", which is how long a scheduled thread will be left to run before the scheduler thinks about switching it, unless it either deschedules itself or else something with higher priority becomes schedulable. The kernel will have low-level timers firing interrupts at the end of a time slice (in addition to interrupts that fire for certain other events such as I/O that could unblock a thread). When a timeslice ends, the scheduler can decide whether to continue with the same thread, or switch to another.

So it looks as though when you sleep, either (a) the scheduler isn't actually setting a timer that will make your thread schedulable at the requested time, it's just waiting for a timeslice, and so the CPU is going idle longer than necessary; or else (b) it is making your thread schedulable at the requested time, but when you gave up execution by sleeping, some other thread of equal priority got in, and the scheduler has no grounds to prefer you over it until it's "your turn" again according to whatever rules the scheduler usually uses to decide what thread to schedule.

69us is pretty short to be an artefact of timeslicing, though.

You seem to have a rudimentary solution - you can delay for very short periods by sitting in a loop checking the time, just like a spinlock. As everyone else is saying, though, in a non-realtime system, more or less by definition you can't demand that the scheduler runs your thread at any specific time. Even in a realtime system, if you're competing with threads of equal priority you may lose, and if you're competing with threads at higher priority you will lose.

过潦 2024-10-24 05:54:55

上面的任何答案都没有提到线程的 PR_SET_TIMERSLACK 参数。默认情况下,它设置为 50 us。有关 PR_SET_TIMERSLACK 的更多信息,请参见:https://man7.org/linux/man -pages/man2/prctl.2.html

您可以使用sudo cat /proc/PID/timerslack_ns检查任何进程的timerslack(不要忘记将PID替换为真实的进程ID)。

具有实时调度程序策略的线程会忽略timerslack值,因此接受的答案有效,因为在SCHED_FIFOSCHED_RR中timerslack被忽略。此外,立即调度优先级高于其他线程的线程也更容易。

也许在某些情况下将timerslack设置为0就足够了(当5us不必每次都严格时)。如果您不想更改调度策略,则可以采用这种方法。

Any answer above doesn't mention about PR_SET_TIMERSLACK parameter of a thread. By default, it's set to 50 us. More about PR_SET_TIMERSLACK here: https://man7.org/linux/man-pages/man2/prctl.2.html.

You can check timerslack for any process with sudo cat /proc/PID/timerslack_ns (don't forget to replace PID with real process ID).

Threads with a real-time scheduler policy ignore timerslack value, so the accepted answer works because in SCHED_FIFO and SCHED_RR timerslack is ignored. Additionally, it's easier to immediately schedule a thread with a priority higher than any other.

Maybe setting timerslack to 0 can be enough in some cases (when 5 us don't have to be strict every single time). This is the way if you don't want to change the scheduling policy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文