【问题标题】:Interrupt sleep by signal通过信号中断睡眠
【发布时间】:2020-10-23 17:09:05
【问题描述】:

我想编写一个 C++ 程序,它应该等待 linux 信号(毫秒分辨率),但我找不到实现这一点的可能性。

以下测试代码应该在 500 毫秒后终止,但它不会。

#include <iostream>
#include <csignal>
#include <unistd.h>
#include <chrono>
#include <future>

using namespace std::chrono_literals;

extern "C" void handler(int s) {

}

int main() {
    std::signal(SIGUSR1, handler);

    bool started = false;
    auto f = std::async(std::launch::async, [&] {
        auto start = std::chrono::high_resolution_clock::now();
        started = true;

        //usleep(1000000);
        sleep(1);
        //std::this_thread::sleep_for(1s);


        std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::high_resolution_clock::now() - start).count() << "ms";

    });
    std::this_thread::sleep_for(500ms);
    std::raise(SIGUSR1);
}

有人知道如何解决这个问题吗?

【问题讨论】:

  • 这很容易使用信号和计时器文件描述符完成,但是这是一个相当高级的主题。从有关 timerfd 和 signalfd 系统调用的 Linux 手册页开始,然后按照自己的方式工作。
  • 程序会显示什么吗?

标签: c++ linux c++17 system-calls usleep


【解决方案1】:

在执行您的信号处理程序后,程序将返回其正常操作。睡眠不会终止,因为它与处理信号的线程不同,因为它在 cmets 中 stark 注释。

相反,这个答案C/C++: How to exit sleep() when an interrupt arrives? 建议使用互斥锁来阻止操作。 try_lock_for

让这变得更容易了

还显示线程 ID(表明它们是不同的线程),这可能是:

#include <iostream>
#include <csignal>
#include <unistd.h>
#include <chrono>
#include <future>

using namespace std::chrono_literals;

std::timed_mutex mtx;

extern "C" void handler(int s) {
    std::cout << std::this_thread::get_id() << " signal handler"
        << std::endl;
    mtx.unlock();
}

int main() {
    std::signal(SIGUSR1, handler);
    auto start_outer = std::chrono::high_resolution_clock::now();

    mtx.lock();

    auto f = std::async(std::launch::async, [&] {
        auto start = std::chrono::high_resolution_clock::now();

        mtx.try_lock_for(std::chrono::milliseconds(1000));

        std::cout << std::this_thread::get_id() << " "
            << std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::high_resolution_clock::now() - start).count() << "ms"
            << std::endl;
    });

    std::this_thread::sleep_for(500ms);
    std::cout << std::this_thread::get_id() << " "
        << std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now() - start_outer).count() << "ms"
        << std::endl;
    std::raise(SIGUSR1);
}

构建、运行、输出

% g++ -std=c++17 test.cpp && ./a.out
0x112cb9dc0 506ms
0x112cb9dc0 signal handler
0x700009e86000 506ms

【讨论】:

  • 没错,但是睡眠应该被信号中止,这样它会更快完成
  • @gerum 低级系统调用可能会被信号中断,但高级sleep_for 不会。例如,您可以看到 libstdc++ 的实现 here 处理 EINTR 并继续休眠任何剩余时间。
  • 我宁愿不使用未定义的行为,并且我在您的代码中看到了两个可能的 UB 来源。首先,您在 signal_handler 中解锁互斥锁,但我不确定这是否与主线程相同,您必须在主线程中解锁互斥锁。而且您在信号处理程序中使用互斥锁,但我不认为在外部“C”函数中使用 C++ 类是明确定义的。
  • @MilesBudnek 是的,我 this_thread sleep 只是为了完整性而添加的,我真的不希望它可以工作,但是带有 sleep 或 usleep 的版本应该可以工作。
  • 旁注:该信号处理程序中有一些危险的东西。 @gerum 只是从不同的上下文中锁定解锁,但除此之外,您可以在信号处理程序中安全地做的事情,至少在 *nix 系统中是well defined and very limited
【解决方案2】:

有了ti7和user4581301的想法,终于找到了解决办法。

在 signal_handler 中使用互斥体的想法,但仅限于允许的系统调用集,我使用信号量。

sem_t *sem_g = nullptr;
extern "C" void handler(int s) {
    if (sem_g)
        sem_post(sem_g);
}

int main() {
    sem_t sem = {};
    sem_init(&sem, 0, 0);
    sem_g = &sem;

    std::signal(SIGUSR1, handler);


    auto f = std::async(std::launch::async, [&] {
        auto start = std::chrono::high_resolution_clock::now();

        auto time_point = std::chrono::system_clock::now() + 10s;
        auto duration = time_point.time_since_epoch();
        auto secs = std::chrono::duration_cast<std::chrono::seconds>(duration);
        auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(duration - secs);

        timespec t{secs.count(), nanos.count()};

        auto r = sem_timedwait(&sem, &t);

        std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::high_resolution_clock::now() - start).count() << "ms";

    });
    std::this_thread::sleep_for(500ms);
    std::raise(SIGUSR1);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-07
    • 1970-01-01
    • 2011-05-02
    • 2019-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多