【问题标题】:C++ Boost::thread - kernel: traps general protectionC++ Boost::thread - 内核:陷阱一般保护
【发布时间】:2018-11-26 19:02:47
【问题描述】:

我的代码使用旧版本的 boost 1.49 可以工作,但那是很多年前的事了。现在我正在使用 boost 1.67

编辑:我的项目包含使用相同二进制文件的服务器/客户端功能。 服务器启动后,我可以发送接收到的命令以启动自定义进程。这是下面显示的代码。

我确定了导致内核陷阱的行:

boost::thread th(Temporal::Acquire, transmit, ECONF);
  • 线程正在启​​动并调用参数中的函数,但启动的线程立即崩溃。 我不明白“一般保护”。

  • 我试图从 try catch (std::exception &e) 中找到更多答案 但它似乎需要另一个捕手...之间没有输出。

  • 试图了解在 libs/thread/src/pthread/thread.cpp 中对 tls_destructor 的处理,但是由于我已经通过将所有 std 替换为 boost 来测试我的代码,但没有解决问题...

  • Valgrind 没有显示任何错误。

有没有办法理解直接终止(无需调用join或中断)?

使用标准线程启动服务器套接字(来自另一个文件)的部分,但我认为这不是问题的根源: 自从我开始我的项目以来,我从来没有在混合 std / boost 时遇到过冲突。

coex.push_back(std::thread(Temporal::Listener, ECONF));

服务器部分:

#include "lobe.hpp"

#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

void    Temporal::Acquire(std::string transmit, Json::Value ECONF)
{
    syslog(LOG_NOTICE, "aquired");
    // Temporal::Transcode p(transmit, ECONF);
}

void    Temporal::Listener(Json::Value ECONF)
{
    socklen_t           t;
    std::string         transmit(100, 0);
    int                 PIPE_local, PIPE_remote, len;
    struct sockaddr_un  local, remote;

    int reuseaddr = 1;
    memset(&local, 0, sizeof(local));

    if((PIPE_local = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
        perror("socket");

    if(setsockopt(PIPE_local, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1)
        perror(strerror(errno));

    local.sun_family = AF_UNIX;
    strncpy(local.sun_path, P_SOCK, sizeof(local.sun_path)-1);
    unlink(P_SOCK);
    len = strlen(local.sun_path) + sizeof(local.sun_family);


    if(bind(PIPE_local, (struct sockaddr *)&local, len) == -1)
        perror("bind");

    if(listen(PIPE_local, 5) == -1)
        perror("listen");

    for(;;)
    {
        syslog(LOG_INFO, "inside SOCK");
        int done, com_Listen, com_Talk;
        t = sizeof(remote);
        if((PIPE_remote = accept(PIPE_local, (struct sockaddr *)&remote, &t)) == -1)
            perror("accept");

        done = 0;
        do
        {
            com_Listen = read(PIPE_remote, &transmit[0], 99);
            if(com_Listen <= 0)
            {
                syslog(LOG_NOTICE, "<<-== %s", transmit.c_str());
                if(com_Listen < 0) perror("recv");
                done = 1;

                syslog(LOG_NOTICE, "received");
                boost::thread th(Temporal::Acquire, transmit, ECONF);
            }
        }while(!done);

        close(PIPE_remote);
        break;
    }
    close(PIPE_local);
    unlink(P_SOCK);

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    //boost::this_thread::sleep_for(boost::chrono::seconds(1));
    Temporal::Listener(ECONF);
}

客户端部分:

systemd coredump 的输出:

Jun 17 22:37:25 bytewild kernel: traps: EIE[8033] general protection ip:44f59c sp:7fd32bffecb0 error:0 in EIE[400000+233000]
Jun 17 22:37:25 bytewild EIE[7699]: aquired
Jun 17 22:37:25 bytewild systemd[1]: Started Process Core Dump (PID 8034/UID 0).
-- Subject: Unit systemd-coredump@49-8034-0.service has finished start-up
-- Defined-By: systemd
-- Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel

-- Unit systemd-coredump@49-8034-0.service has finished starting up.
-- 
-- The start-up result is RESULT.
Jun 17 22:37:25 bytewild systemd-coredump[8041]: Failed to get ACL: Operation not supported
Jun 17 22:37:26 bytewild systemd-coredump[8041]: Process 7699 (EIE) of user 1000 dumped core.

     Stack trace of thread 8033:
     #0  0x000000000044f59c tls_destructor (/data/dev/in/native/projects/eie/build/bin/EIE)
     #1  0x000000000045092a thread_proxy (/data/dev/in/native/projects/eie/build/bin/EIE)
     #2  0x0000000000500155 start_thread (/data/dev/in/native/projects/eie/build/bin/EIE)
     #3  0x00000000005707ff __clone (/data/dev/in/native/projects/eie/build/bin/EIE)

     Stack trace of thread 7700:
     #0  0x0000000000503623 __pthread_cond_timedwait (/data/dev/in/native/projects/eie/build/bin/EIE)
     #1  0x000000000041214b _ZN5boost18condition_variable13do_wait_untilERNS_11unique_lockINS_5mutexEEERKNS_6detail23mono_platform_timepointE (/data/dev/in/native/projects/eie/build/bin/EIE)
     #2  0x000000000040ebe4 _ZN8Temporal8ListenerEN4Json5ValueE (/data/dev/in/native/projects/eie/build/bin/EIE)
     #3  0x000000000041f58e _ZSt13__invoke_implIvPFvN4Json5ValueEEJS1_EET_St14__invoke_otherOT0_DpOT1_ (/data/dev/in/native/projects/eie/build/bin/EIE)
     #4  0x00000000004eb0ef execute_native_thread_routine (/data/dev/in/native/projects/eie/build/bin/EIE)
     #5  0x0000000000500155 start_thread (/data/dev/in/native/projects/eie/build/bin/EIE)
     #6  0x00000000005707ff __clone (/data/dev/in/native/projects/eie/build/bin/EIE)

     Stack trace of thread 7699:
     #0  0x0000000000504a21 __nanosleep (/data/dev/in/native/projects/eie/build/bin/EIE)
     #1  0x000000000056bfea __sleep (/data/dev/in/native/projects/eie/build/bin/EIE)
     #2  0x0000000000406e91 main (/data/dev/in/native/projects/eie/build/bin/EIE)
     #3  0x0000000000506dfa __libc_start_main (/data/dev/in/native/projects/eie/build/bin/EIE)
     #4  0x000000000040790a _start (/data/dev/in/native/projects/eie/build/bin/EIE)

这是我的系统的示意图预览,以了解问题:

对不起,如果这很明显,但我很难过。有什么线索吗?

【问题讨论】:

  • 我看到你的线程是在块的末尾创建的。看这个,似乎线程在完成工作之前就被破坏了。如果您在创建线程后分离线程。它解决了您的异常问题吗?
  • 嗨,Feddy,你说得对,我认为是这样,但添加 th.detach() 并不能解决问题。我刚刚更新了我的帖子,以解释我对项目的更多期望。

标签: c++ boost-thread coredump


【解决方案1】:

查看您的代码,我认为问题出在这段代码中。

do
        {
            com_Listen = read(PIPE_remote, &transmit[0], 99);
            if(com_Listen <= 0)
            {
                syslog(LOG_NOTICE, "<<-== %s", transmit.c_str());
                if(com_Listen < 0) perror("recv");
                done = 1;

                syslog(LOG_NOTICE, "received");
                boost::thread th(Temporal::Acquire, transmit, ECONF);
            }
        }while(!done);

具体来说,您创建boost::thread 的方式。这是一个基于堆栈的变量。一旦你启动你的线程,它的对象就会被销毁并调用DTor

我在 thread 的 boost 实现方面没有太多经验,但我使用过 std::thread,它主要以 boost 实现为模型。查看他们的课程documentation,有一个效果部分列出了销毁正在运行的线程的影响。

效果: - 如果定义了 BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE: 如果线程是可连接的,则调用 detach(),已弃用 - 如果定义了 BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE: 如果线程是可连接的,则调用 std::terminate。销毁 *this。

看起来默认是调用terminate() 就像std::thread 为被破坏的可连接线程所做的那样。看起来旧的行为是自动分离那些。

编辑 阅读当前与 1.49 的析构函数文档。

1.49 效果: 如果 *this 有关联的执行线程,则调用 detach()。销毁 *this。

现在,去阅读当前的相同代码(上面列出的)。它声明它现在默认为terminate

编辑2

我的建议是不再在每次从套接字接收输入时启动新线程。相反,我建议创建一个具有恒定数量后台线程的工作队列。每次收到新事件时,只需将工作对象添加到队列中,其中一个后台线程将处理响应。

#include <chrono>
#include <condition_variable>
#include <deque>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

// Synchrnoized Output
std::mutex stdm;
template <typename T>
void Log(T const& t) {
    std::lock_guard<std::mutex> lk(stdm);
    std::cout << t << std::endl;
}

// Task Queue
// Has user provided list of task, which are handled on background threads
template <class T, int N = 4>
class TaskQueue {
    std::deque<T> work;                 // Holds work
    std::vector<std::thread> threads;   // Holds worker threads
    std::mutex m;                       // Holds lock for work container/running
    std::condition_variable cv;         // Worker threads wait on this
    bool running;                       // Inform the task queue if its running

    public:
    // Constructor, spins up worker threads and waits for work
    TaskQueue() : running{true} {
        threads.reserve(N);
        for (int i = 0; i < N; ++i) {
            // Build worker threads
            threads.emplace_back([&]() {
                // Normal running before queue destruction
                while (running) {
                    {
                        std::unique_lock<std::mutex> lk(
                            m);
                        cv.wait(lk, [&] {
                            return !running ||
                                   work.size() != 0;
                        });

                        // Extract work && Update work
                        // Queue
                        T t = std::move(work.front());
                        work.pop_front();

                        // Release lock before
                        // performing work
                        lk.unlock();

                        // Peform work
                        t();
                    }
                }

                // Empty Queue on destrucrtion
                bool hasMoreWork = true;
                do {
                    std::unique_lock<std::mutex> lk(m);
                    if ((hasMoreWork = work.size() > 0)) {
                        // Manage Work
                        T t = std::move(work.front());
                        work.pop_front();

                        // has more?
                        hasMoreWork = work.size() > 0;

                        // release lock
                        lk.unlock();

                        // perform work
                        t();
                    }
                } while (hasMoreWork);
            });
        }
    }

    ~TaskQueue() {
        // Inform queue its closing
        {
            std::lock_guard<std::mutex> lk(m);
            running = false;
        }

        // Inform all threads of change
        cv.notify_all();

        // Clear out remainings objects
        int workObjects = 0;
        bool queueCleared = false;
        do {
            {
                std::lock_guard<std::mutex> lg(m);
                queueCleared = (workObjects = work.size()) == 0;
                Log("Queue Has Remaining: " +
                    std::to_string(workObjects));
            }

            // Give worker threads time to work
            std::this_thread::sleep_for(
                std::chrono::milliseconds(250));
        } while (!queueCleared);

        // If any threads are still processing, join them to the current
        // thread or else terminate() is called
        for (int i = 0; i < N; i++) {
            if (threads[i].joinable()) threads[i].join();
        }
    }

    template <class... Args>
    void emplace_back(Args&&... args) {
        {
            std::lock_guard<std::mutex> lk(m);
            if (running)
                work.emplace_back(std::forward<Args>(args)...);
        }
        cv.notify_one();
    }
};

// The actual task that performs work
struct Task {
    std::string transmit;
    std::string ECONF;

    Task() : transmit(""), ECONF("") {}
    Task(std::string&& t, std::string&& e) : transmit(t), ECONF(e) {}

    void operator()() {
        std::thread::id tid = std::this_thread::get_id();
        std::hash<std::thread::id> hasher;
        Log(transmit + ':' + ECONF +
            " Process Time: 500ms One Thread: " +
            std::to_string(hasher(tid)));

        // Fake work to consume thread
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
};

int main() {
    TaskQueue<Task> tp;
    for (int i = 0; i < 200; ++i) {
        // Add work
        tp.emplace_back("Transmit: " + std::to_string(i),
                "ECONF: " + std::to_string(i));

        // Simulate waiting for the next event from socket
        std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
    return 0;
}

可以进行大量改进,但这应该会给您一个粗略的概述。希望这会有所帮助。

【讨论】:

  • 是的,我从 std::thread 知道这些要求,但自 boost 1.49(5 年前)和现在的 1.67 以来我没有更改我的代码,而且我从未使用过 .join()终止 boost 线程。甚至,当我尝试手动终止它时,也会生成相同的核心转储。
  • @MTroy 默认行为已更改。如果您查看 1.49 中的文档,它指出默认行为是调用 detatch。如果您查看最近的文档,请致电terminate。您的代码没有改变,thread 的 boost 实现已经改变。
  • 确实!但这意味着我不能用这个改变做我想做的事?我需要发送许多命令以并行处理。如果 boost:thread 现在立即终止,如何并行化每个线程? detach() 不起作用,它无论如何都会崩溃
  • 在分离之前确保线程是可连接的。或者使用工作队列。我不在我的电脑旁,但我稍后会用一个例子更新我的答案。
  • th.joinable() 说“真”,但 detach() 仍然不适用。您认为忘记 boost 线程并仅使用 std pthread 是一个更好的主意吗?
猜你喜欢
  • 1970-01-01
  • 2015-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
  • 1970-01-01
相关资源
最近更新 更多