C++ Boost::thread - 内核:陷阱一般保护
C++ Boost::thread - kernel: traps general protection
我的代码曾使用旧版本的 boost 1.49 运行,但那是很多年前的事了。现在我正在使用 boost 1.67
编辑:我的项目包含使用相同二进制文件的 server/client 功能。
服务器启动后,我可以发送接收到的命令来启动自定义进程。这是下面显示的代码。
我确定了导致内核陷阱的行:
boost::thread th(Temporal::Acquire, transmit, ECONF);
线程正在启动并调用参数中的函数,但启动的线程立即崩溃。
我不明白 "general protection".
我试图从 try catch (std::exception &e) 中找到更多答案
但它似乎需要另一个捕手...没有输出。
试图理解 libs/thread/src/pthread/thread.cpp 中对 tls_destructor 的处理,但是因为我已经通过替换所有 std 来测试我的代码以仅提升而没有解决问题。 ..
Valgrind 完全没有显示错误。
有没有办法理解直接终止(不调用join或interrupt)?
使用标准线程启动服务器套接字(从另一个文件),但我认为这不是问题的根源:
自从我开始我的项目以来,我从来没有遇到混合 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)
这里是我的系统的示意图预览,以了解问题:
很抱歉,如果这很明显,但我很难过。有什么线索吗?
查看您的代码,我认为问题出在这段代码中。
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 实现为模型。查看他们的 class documentation 有一个效果部分列出了破坏 运行 线程的影响。
Effects:
- if defined BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE:
If the thread is joinable calls detach(), DEPRECATED
- if defined BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE:
If the thread is joinable calls to std::terminate. Destroys *this.
看起来默认调用 terminate()
就像 std::thread
对被销毁的可连接线程所做的那样。看起来旧的行为是自动分离那些。
编辑
阅读当前与 1.49 的析构函数文档。
1.49
效果:
如果 *this 有关联的执行线程,则调用 detach()。销毁 *this.
现在,去阅读 current(上面列出的)的相同代码。它声明它现在默认为 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;
}
可以进行很多改进,但这应该给您一个粗略的概述。希望这有帮助。
我的代码曾使用旧版本的 boost 1.49 运行,但那是很多年前的事了。现在我正在使用 boost 1.67
编辑:我的项目包含使用相同二进制文件的 server/client 功能。 服务器启动后,我可以发送接收到的命令来启动自定义进程。这是下面显示的代码。
我确定了导致内核陷阱的行:
boost::thread th(Temporal::Acquire, transmit, ECONF);
线程正在启动并调用参数中的函数,但启动的线程立即崩溃。 我不明白 "general protection".
我试图从 try catch (std::exception &e) 中找到更多答案 但它似乎需要另一个捕手...没有输出。
试图理解 libs/thread/src/pthread/thread.cpp 中对 tls_destructor 的处理,但是因为我已经通过替换所有 std 来测试我的代码以仅提升而没有解决问题。 ..
Valgrind 完全没有显示错误。
有没有办法理解直接终止(不调用join或interrupt)?
使用标准线程启动服务器套接字(从另一个文件),但我认为这不是问题的根源: 自从我开始我的项目以来,我从来没有遇到混合 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)
这里是我的系统的示意图预览,以了解问题:
很抱歉,如果这很明显,但我很难过。有什么线索吗?
查看您的代码,我认为问题出在这段代码中。
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 实现为模型。查看他们的 class documentation 有一个效果部分列出了破坏 运行 线程的影响。
Effects: - if defined BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE: If the thread is joinable calls detach(), DEPRECATED - if defined BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE: If the thread is joinable calls to std::terminate. Destroys *this.
看起来默认调用 terminate()
就像 std::thread
对被销毁的可连接线程所做的那样。看起来旧的行为是自动分离那些。
编辑 阅读当前与 1.49 的析构函数文档。
1.49 效果: 如果 *this 有关联的执行线程,则调用 detach()。销毁 *this.
现在,去阅读 current(上面列出的)的相同代码。它声明它现在默认为 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;
}
可以进行很多改进,但这应该给您一个粗略的概述。希望这有帮助。