当单独的文件中的类时,使用asio独立的Segfault

Neo*_*ana 16 c++ boost boost-asio

以下是我能得到的最小例子.它确实需要在单独的文件中,因为这似乎是导致分段错误错误的原因.

我正在使用带有Asio独立1.10.6的Mingw x32 4.8.1.我还测试了TDM GCC 4.7.1和M​​ingw x64 4.8.1.Windows下的所有这些都产生相同的段错误.在Linux下使用最新版本的GCC没有这样的问题.

编辑:我刚刚完成了对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文件,我得到的一个segfault Server类的构造函数.

我进一步回去把所有东西都放回去main.cpp,开始逐一移动每个班级.segfault在最后一个类之后开始出现,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
Run Code Online (Sandbox Code Playgroud)

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))
{
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Client.cpp

#include "Client.hpp"
using namespace network;

#include <iostream>

Client::Client(asio::io_service& ioService)
: m_timer(ioService)
{
}
Run Code Online (Sandbox Code Playgroud)

main.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;
}
Run Code Online (Sandbox Code Playgroud)

这是来自GDB的callstack:

#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)
Run Code Online (Sandbox Code Playgroud)

最后这是内存博士的输出:

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
Run Code Online (Sandbox Code Playgroud)

我只是不明白为什么我会遇到段错误.即使在评论出所有有意义的代码之后,它仍然会发生.


编辑

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


编辑2

我用TDM GCC 4.7.1,Mingw Builds x64 4.8.1和Mingw Builds x32 4.8.1进行了测试.所有这些都有相同的结果.


编辑3

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


编辑4

我现在已经完全删除了Server.hpp,Server.cpp并且已更新main.cpp到此:

main.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;
}
Run Code Online (Sandbox Code Playgroud)

我仍然得到段错误,而callstack反映了缺乏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))
Run Code Online (Sandbox Code Playgroud)

编辑6

我现在开始超越自己的能力,但我已经设法找到了一些问题(我正在学习一些很酷的新东西).

看来,当asio::io_service&创建一个需要创建的对象时,它会向其中添加"服务" io_service.这些在所有io_service实例中都是静态的.因此,当发出服务请求时,会有一个迭代的循环,它似乎是已创建服务的链接列表.如果尚未创建所请求的服务; 然后创建它.

此信息来自查看的参考io_serviceservice_registry.ipp(第111行).

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

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

为什么这个成员有价值0xffffffff超出我.我的理解是,只有旧/奇怪的机器保留了这个地址作为null指针...但我承认我可能会离开.

我这样做很快就检查了一下:

int* p = nullptr;
if(p)
    std::cout << "something" << std::endl;
Run Code Online (Sandbox Code Playgroud)

并设置断点来读取值.值的值p0x0,而不是0xffffffff.

因此,我在构造函数for service_registry(asio/detail/impl/service_registry.hpp)和析构函数(在它显式调用某处)和三种方法上设置断点do_has_service,do_use_service并且do_add_service.我的想法是尝试跟踪什么时候first_service_得到坏的价值.

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

我认为这意味着某些东西已经破坏了堆栈并改变了地址first_service_.但是,我只是一个业余爱好者......

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


编辑7

好的,所以我现在发现,如果我编译ASIO_DISABLE_THREADS我不再得到段错误!

但是,这会导致异常被抛出,因为即使我已经禁用了线程,我也试图使用线程.我认为这意味着我将被限制为同步调用和没有异步调用.(即,使用asio有什么意义?)

这里参考资料确实如此ASIO_DISABLE_THREADS

显式禁用Asio的线程支持,无论Boost是否支持线程.

所以我认为这意味着这个定义会阻止asio使用线程而不管是否提升; 这是有道理的.

为什么线程会导致问题,我不知道.我并不热衷于钻研这么远.


我放弃

我放弃了asio.在查看代码和文档之后,它似乎已经开发出来了,而不是作为一个独立的库.显然,你需要使用Boost而不是C++ 11,我只是不在意.

最好的C/C++网络库看起来有很多替代品.

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

考虑到asio看起来像是标准库中的主要候选者,或者至少它的味道,坚持下去可能是个好主意.

huy*_*itw 5

我想分割故障发生时,因为不匹配之间的ASIO类型定义的Server.hppClient.hpp.在你的情况,如果推动变化的typedef取决于由集定义这只能发生<string>,<memory><vector>.

我建议尝试的是:

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

    #include <string>
    #include <memory>
    #include <vector>
    
    #define ASIO_STANDALONE
    #include <asio.hpp>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 或者只是在头文件中的任何其他包含之前包含asio.hpp,并从main.cpp中删除include.