【问题标题】:Segfault with asio standalone when classes in separate files当类在单独的文件中时,带有 asio 的 Segfault 独立
【发布时间】:2015-10-06 13:12:13
【问题描述】:

下面是我能得到的最小示例。它确实需要位于单独的文件中,因为这似乎是导致分段错误错误的原因。

我正在使用 Mingw x32 4.8.1 和 Asio 独立 1.10.6 。我还使用 TDM GCC 4.7.1 和 Mingw x64 4.8.1 进行了测试。所有这些在 Windows 下都会产生相同的段错误。最新版本的 GCC 在 Linux 下不存在这样的问题。

编辑:我刚刚完成了对 Mingw-w64 5.2.0(它的 Mingw-Builds 构建)的测试。同样的问题。

我还测试了在新虚拟机上使用 TDM GCC 4.8.1 编译它并得到相同的段错误。编辑:我现在也在使用 TDM GCC 4.8.1 的完全不同的机器上进行了测试

在所有情况下,我都使用-std=c++11-g-Wall。我也用-g 编译并得到相同的结果。我需要 C++11 标志,因为我不想依赖于 boost,只需要 asio。

使用单个main.cpp 文件中的以下代码没有问题,并且程序似乎按预期运行。但是,如果我将每个类放入它自己的 *.hpp*.cpp 文件中,我会在 Server 类构造函数中遇到段错误。

我进一步返回并将所有内容放回main.cpp,并开始逐个移动每个班级。段错误在最后一课之后开始发生,Client 被放入它自己的文件中。

此外,当我将所有类放入一个文件并移动它们等时,我确保任何不需要的目标文件都没有链接到我的 .exe。

此代码开始时要大得多,但已缩小到此范围。

Server.hpp

#ifndef SERVER_HPP_INCLUDED
#define SERVER_HPP_INCLUDED

#include <string>
#include <memory>

#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;

namespace network
{
    class Server
    {
    public:

        Server(asio::io_service& ioService, uint16_t port);

    private:

        tcp::acceptor m_acceptor;
    };
}

#endif // SERVER_HPP_INCLUDED

Server.cpp

#include "Server.hpp"
using namespace network;

#include <iostream>

Server::Server(asio::io_service& ioService, uint16_t port)
: m_acceptor(ioService, tcp::endpoint(tcp::v4(),port))
{
}

Client.hpp

#ifndef CLIENT_HPP_INCLUDED
#define CLIENT_HPP_INCLUDED

#include <vector>

#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;

namespace network
{
    class Client
    {
    public:

        Client(asio::io_service& ioService);

    private:

        asio::steady_timer m_timer;
    };
}

#endif // CLIENT_HPP_INCLUDED

Client.cpp

#include "Client.hpp"
using namespace network;

#include <iostream>

Client::Client(asio::io_service& ioService)
: m_timer(ioService)
{
}

ma​​in.cpp

#include <iostream>

#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;

#include "Server.hpp"

int main()
{
    try
    {
        uint16_t peerRequestPort = 63000;

        asio::io_service io_service;

        network::Server server(io_service,peerRequestPort);
    }
    catch(std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }

    return 0;
}

这是来自 GDB 的调用堆栈:

#0 00406729 asio::detail::service_registry::keys_match(key1=..., key2=...) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:89)
#1 ??   0x0040696e in asio::detail::service_registry::do_use_service (this=0x5d2f10, key=..., factory=0x406b44 <asio::detail::service_registry::create<asio::socket_acceptor_service<asio::ip::tcp> >(asio::io_service&)>) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:113)
#2 004068B6 asio::detail::service_registry::use_service<asio::socket_acceptor_service<asio::ip::tcp> >(this=0x5d2f10) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.hpp:47)
#3 00403857 asio::use_service<asio::socket_acceptor_service<asio::ip::tcp> >(ios=...) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/impl/io_service.hpp:32)
#4 004039B3 asio::basic_io_object<asio::socket_acceptor_service<asio::ip::tcp>, true>::basic_io_object(this=0x28fe48, io_service=...) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_io_object.hpp:182)
#5 00403B29 asio::basic_socket_acceptor<asio::ip::tcp, asio::socket_acceptor_service<asio::ip::tcp> >::basic_socket_acceptor(this=0x28fe48, io_service=..., endpoint=..., reuse_addr=true) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_socket_acceptor.hpp:137)
#6 00401D3B network::Server::Server(this=0x28fe48, ioService=..., port=63000) (F:\GameDev\Dischan\Tests\Server.cpp:7)
#7 004018F1 main() (F:\GameDev\Dischan\Tests\main.cpp:17)

最后是 Dr Memory 的输出:

Dr. Memory version 1.8.0 build 8 built on Sep  9 2014 16:27:02
Dr. Memory results for pid 5296: "tests.exe"
Application cmdline: "tests.exe"
Recorded 108 suppression(s) from default C:\Program Files (x86)\Dr. Memory\bin\suppress-default.txt

Error #1: UNADDRESSABLE ACCESS: reading 0x00000007-0x0000000b 4 byte(s)
# 0 asio::detail::service_registry::keys_match                         [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:89]
# 1 asio::detail::service_registry::do_use_service                     [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:113]
# 2 asio::detail::service_registry::use_service<>                      [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.hpp:47]
# 3 asio::use_service<>                                                [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/impl/io_service.hpp:32]
# 4 asio::basic_io_object<>::basic_io_object                           [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_io_object.hpp:182]
# 5 asio::basic_socket_acceptor<>::basic_socket_acceptor               [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_socket_acceptor.hpp:137]
# 6 network::Server::Server                                            [F:/GameDev/Dischan/Tests/Server.cpp:7]
# 7 main                                                               [F:/GameDev/Dischan/Tests/main.cpp:17]
Note: @0:00:00.780 in thread 7464
Note: instruction: mov    0x04(%eax) -> %eax

Error #2: LEAK 36 direct bytes 0x02530860-0x02530884 + 124 indirect bytes
# 0 replace_operator_new                       [d:\drmemory_package\common\alloc_replace.c:2609]
# 1 asio::io_service::io_service               [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/impl/io_service.ipp:39]
# 2 main                                       [F:/GameDev/Dischan/Tests/main.cpp:15]

===========================================================================
FINAL SUMMARY:

DUPLICATE ERROR COUNTS:

SUPPRESSIONS USED:

ERRORS FOUND:
      1 unique,     1 total unaddressable access(es)
      0 unique,     0 total uninitialized access(es)
      0 unique,     0 total invalid heap argument(s)
      0 unique,     0 total GDI usage error(s)
      0 unique,     0 total handle leak(s)
      0 unique,     0 total warning(s)
      1 unique,     1 total,    160 byte(s) of leak(s)
      0 unique,     0 total,      0 byte(s) of possible leak(s)
ERRORS IGNORED:
     14 potential error(s) (suspected false positives)
         (details: C:\Users\User\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.5296.000\potential_errors.txt)
     12 potential leak(s) (suspected false positives)
         (details: C:\Users\User\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.5296.000\potential_errors.txt)
     24 unique,    24 total,   2549 byte(s) of still-reachable allocation(s)
         (re-run with "-show_reachable" for details)
Details: C:\Users\User\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.5296.000\results.txt

我只是不明白为什么会出现段错误。即使注释掉所有有意义的代码,它仍然会出现。


编辑

我已经编辑了上面的代码,以表明只有 Server 构造函数似乎会导致问题以及每个文件的内容。 (我再次确保只有这些目标文件被编译和链接)。


编辑 2

我已经使用 TDM GCC 4.7.1、Mingw Builds x64 4.8.1 和 Mingw Builds x32 4.8.1 对此进行了测试。所有的结果都是一样的。


编辑 3

我进一步减少了代码。现在,在Client 中,如果我删除任何需要构造asio::io_service&amp;asio 对象,那么就没有段错误。但是到目前为止我尝试过的任何asio 类型都产生了相同的段错误。这在 Server 类中不是问题,例如,它有一个 asio::acceptor。遥远的事情是没有创建Client 的实例,所以为什么它会影响程序并在Servers 构造函数中产生段错误是很奇怪的。


编辑 4

我现在已经完全删除了 Server.hppServer.cpp 并将 main.cpp 更新为:

ma​​in.cpp

#include <iostream>

#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;

int main()
{
    try
    {
        uint16_t peerRequestPort = 63000;

        asio::io_service io_service;

        auto protocol = tcp::v4();
        tcp::endpoint endpoint(protocol,peerRequestPort);
        tcp::acceptor m_acceptor(io_service, endpoint);

    }
    catch(std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }

    return 0;
}

我仍然得到段错误并且调用堆栈反映了缺少Server 构造函数。段错误仍然在同一个地方。 DrMemory 结果看起来也差不多。

和以前一样,如果我不链接Client 目标文件,我没有问题。


编辑 5

根据要求,这是来自 Code::Blocks 的构建日志

g++.exe -std=c++11 -Wall -D_WIN32_WINNT=0x0501 -g -I..\..\asio-1.10.6\asio-1.10.6\include -c F:\GameDev\Dischan\Tests\Client.cpp -o obj\Debug\Client.o
g++.exe -std=c++11 -Wall -D_WIN32_WINNT=0x0501 -g -I..\..\asio-1.10.6\asio-1.10.6\include -c F:\GameDev\Dischan\Tests\main.cpp -o obj\Debug\main.o
g++.exe  -o Build\Debug\Windows\Tests.exe obj\Debug\Client.o obj\Debug\main.o   -lws2_32 -lwsock32
Output file is Build\Debug\Windows\Tests.exe with size 723.02 KB
Process terminated with status 0 (0 minute(s), 3 second(s))
0 error(s), 0 warning(s) (0 minute(s), 3 second(s))

编辑 6

我现在开始超出我的能力范围,但我设法找到了一些问题(而且我正在学习一些很酷的新东西)。

似乎在创建需要asio::io_service&amp; 的对象时,它会将“服务”添加到io_service。这些在所有 io_service 实例中都是静态的。因此,当发出服务请求时,会出现一个循环,通过该循环似乎是已创建服务的链接列表。如果请求的服务尚未创建;然后创建它。

此信息来自 reference for io_service 和查看 service_registry.ipp(第 111 行)。

这是通过调用service_registry::do_use_service 在内部完成的。 service_registry 有一个名为 first_service_ 的成员,类型为 asio::io_service::service*。第一个服务应该有一个名为next_ 的成员,这是我提到的链表部分。

然而,在第一次调用service_registry::do_use_service 时(构造asio::acceptor 时),first_service_ 成员的值为0xffffffff,这显然是不正确的。所以我相信这是段错误的根源。

为什么这个成员有0xffffffff 的值是我无法理解的。我的理解是只有旧的/古怪的机器为null 指针保留了这个地址......但我承认我可能会走得很远。

我只是通过这样做快速检查了这一点:

int* p = nullptr;
if(p)
    std::cout << "something" << std::endl;

并设置断点来读取值。 p 的值是 0x0,而不是 0xffffffff

所以,我在service_registry (asio/detail/impl/service_registry.hpp) 的构造函数和析构函数(以防它在某处显式调用)以及do_has_servicedo_use_servicedo_add_service 三个方法上设置了一个断点.我的想法是尝试跟踪 first_service_ 在什么时候得到错误值。

我没有运气。这些是唯一可能改变first_service_ 值的地方。

我认为这意味着某些东西损坏了堆栈并更改了first_service_ 的地址。但是,我只是个业余爱好者……

我确实检查了构造函数的 this 指针的地址与用于调用 do_use_service 的地址相同,以确保没有创建两个实例或类似的东西。


编辑 7

好的,所以我现在发现,如果我使用 ASIO_DISABLE_THREADS 进行编译,我将不再遇到段错误!

但是,这会导致引发异常,因为我尝试使用线程,即使我已禁用它们。我的意思是我将被限制为同步调用而不是异步调用。 (即,使用 asio 有什么意义?)

reference material here 确实说ASIO_DISABLE_THREADS

明确禁用 Asio 的线程支持,与 Boost 是否支持线程无关。

所以我认为这个定义会阻止 asio 使用线程,无论是否提升;这是有道理的。

为什么线程会导致问题,我不知道。我不热衷于深入研究。


我放弃了

我放弃了asio。在查看了代码和文档之后,它似乎是在开发时考虑到了 boost-so 而不是作为一个独立的库。显然到了需要在 C++11 上使用 Boost 的地步,我只是不想这样做。

Best C/C++ Network Library 看起来有很多选择。

说实话,考虑到我将获得的控制权,在我自己的线程中运行我自己的同步套接字调用听起来是一个更好的主意。至少在 asio 进入标准库并在 Mingw-w64 中实现之前。

考虑到 asio 看起来像是标准库中的主要候选者,或者至少是它的风格,坚持使用它可能是个好主意。

【问题讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • 我很担心你的健康
  • 这似乎与您的问题非常相似:stackoverflow.com/questions/28427000/…
  • @Jonathan 这听起来像同样的问题;但是,没有比这里更多的答案了。 :(
  • 这可能是一个愚蠢的评论。但是你不应该调用io服务对象的run方法吗?

标签: c++ boost boost-asio


【解决方案1】:

我认为出现分段错误是因为Server.hppClient.hpp 之间的 asio 类型定义不匹配。在您的情况下,只有当 boost 根据 &lt;string&gt;&lt;memory&gt;&lt;vector&gt; 设置的定义更改 typedefs 时,才会发生这种情况。

我建议尝试的是:

  1. 在包含 asio.hpp 之前,在 Server/Client.hpp 和 main.cpp 中包含相同的标头:

    #include <string>
    #include <memory>
    #include <vector>
    
    #define ASIO_STANDALONE
    #include <asio.hpp>
    
  2. 或者在头文件中的任何其他包含之前简单地包含 asio.hpp,然后从 main.cpp 中删除包含。

【讨论】:

  • 运行g++ Client.cpp Server.cpp main.cpp 不会导致崩溃,而使用-c 和单独的链接命令会导致崩溃。你能解释一下吗?
  • 使用哪个#include-setup?此外,我们不应该忘记,如果这是一个线程问题,则可能很难调试并导致各种奇怪的解决方案放置 f.e. printf 某处或等待满月似乎只能为您的机器修复它:)
  • 在提问者的设置中。
  • 好的,那不是只确认在单独编译它们时使用了不同的 typedef 或函数声明,不是吗?甚至链接的顺序也可能会改变行为。
  • 你的意思是,在这种情况下,gcc 在编译源之前会连接它们?
【解决方案2】:

从我的 POV 来看,这是一个 gcc 错误。如果您使用clang++ 而不是g++.exe,即使不摆弄#include 的顺序,崩溃也会消失。在单个调用中将所有源文件传递给g++ 也会使崩溃消失。这让我认为 gcc 的 COFF 目标文件发射 有问题,因为 clang 使用来自同一个 mingw 发行版的链接器。

所以,要么使用 clang,要么应用 @Wouter Huysentruit 的回答中的解决方法。

请注意,最新的 clang 版本 (3.7.0) 有另一个与您的问题无关的错误,导致链接失败。我不得不使用nightly snapshot,反正还是很稳定的。

【讨论】:

    【解决方案3】:

    我有类似的问题。我的应用程序在 win_lock::lock() 上崩溃了,但 win_lock::win_lock() 构造函数从未被调用!

    将 -D_POSIX_THREADS 添加到编译器标志后,它就开始工作了。

    我在win7上使用mingw-w64-i686-7.1.0-posix-dwarf-rt_v5-rev0

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-22
      • 1970-01-01
      • 2018-09-11
      相关资源
      最近更新 更多