在使用fastdfs时,编写数据上传代码时,遇到一个坑。最终根据指针对应的内存布局定位到一个其client API的一个坑,值得记录一下。
具体是在 tracker_connect_server() 这个API上,其是一个宏,具体定义如下

 #define tracker_connect_server(pTrackerServer, err_no) \
tracker_connect_server_ex(pTrackerServer, g_fdfs_connect_timeout, err_no)

tracker_connect_server_exs 声明如下

 ConnectionInfo *tracker_connect_server_ex(ConnectionInfo *pTrackerServer, const int connect_timeout, int *err_no);

在tracker_proto.c中实现,其返回值将在后面数据上传操作中的链接句柄。

当配置不使用连接池的时候,连接操作成功时,会将 pTrackerServer 参数原样返回,一般使用者会习惯性地保存该指针句柄供后用。我自己仿照着范例代码使用,结果就掉坑里了。

如此设计实现,就极易产生勿用,如下:
当 tracker_connect_server_ex() 的 pTrackerServer 参数传递的对象,后续被销毁之后,用户是不知道刚才通过返回值拿到的指针句柄指向的对象是已经不复存在的。
后续继续使用该已经失效的指针句柄进行数据上传操作时,结果就不可预知了。

实际上在其工程test目录下的范例代码 dfs_func.c 中, upload_file() 函数 pTrackerServer 参数就传递了一个栈上的临时对象,但其在 upload_file() 中立即使用返回值的指针句柄调用 storage_upload_by_filebuff1() 进行数据上传,所以对应返回值指针句柄指向的对象,在此过程中都一直有效,而没有造成问题。

但自己就没有这么幸运了。我类似地仿照 upload_file() 给 tracker_connect_server() 传递了一个栈上的临时对象时,拿到其返回值做保存之后,将从当前调用函数返回了,
后续使用刚才保存的指针句柄调用 storage_do_upload_file1() 进行数据上传操作,总是出现下面的错误打印
[2016-03-05 17:46:26] ERROR - file: storage_client.c, line: 933, send data to storage server :0 fail, errno: 88, error info: Socket operation on non-socket

于是就开始了一番debug,可从 tracker_connect_server() 调用返回的地方,一路确认到调用 storage_do_upload_file1() 的地方,该指针句柄值都没有发生变化,但其指向的内存却被异常地清零了,一时见鬼了!后来在 gdb 中对给 storage_do_upload_file1() 传递的前两个参数值 print 了一把,发现了一点端倪,如下

 (gdb) p  ((struct fastdfs_priv*)&dfs[1])->tracker_conn
$4 = (ConnectionInfo *) 0x613150
(gdb) p ((struct fastdfs_priv*)&dfs[1])->storage_conn
$5 = (ConnectionInfo *) 0x7fffffffe4e0
(gdb)

根据地址区间,很明显 tracker_conn 指向的内存在堆上,而 storage_conn 指向的内存在栈里!这就奇了怪,storage_conn 值源头是来自 tracker_connect_server() 啊,难道其给我返回了一个栈上的临时对象地址?其作者应该不会犯这种弱智错误吧?去看了 tracker_connect_server_ex() 的代码之后,顿时吐血!太坑了吧!作者自己难道没想过上面的这些问题么?而且还在范例代码中,这么用?明显误导人么??!!!

知道原因之后,立即将 tracker_connect_server() 第一个参数传递的对象改为不是临时的后,问题解决!

刚才的问题,之所以能发现突破点,还是根据指针值对应的内存布局来确定的。这又一次说明了内存等的相关的基础知识是非常重要的!!!

根据内存布局定位的一个fastdfs坑的更多相关文章

  1. 内存对齐与ANSI C中struct型数据的内存布局 【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3032209.html 当在C中定义了一个结构类型时,它的大小是否等于各字段(field)大小之和?编译器将 ...

  2. 内存对齐与ANSI C中struct型数据的内存布局

    当在C中定义了一个结构类型时,它的大小是否等于各字段(field)大小之和?编译器将如何在内存中放置这些字段?ANSI C对结构体的内存布局有什么要求?而我们的程序又能否依赖这种布局?这些问题或许对不 ...

  3. 浅析内存对齐与ANSI C中struct型数据的内存布局-内存对齐规则

    这些问题或许对不少朋友来说还有点模糊,那么本文就试着探究它们背后的秘密. 首先,至少有一点可以肯定,那就是ANSI C保证结构体中各字段在内存中出现的位置是随它们的声明顺序依次递增的,并且第一个字段的 ...

  4. HotSpot源码分析之C++对象的内存布局

    HotSpot采用了OOP-Klass模型来描述Java类和对象.OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象的具体类型.为了更好理解这个模型, ...

  5. jvm学习记录-对象的创建、对象的内存布局、对象的访问定位

    简述 今天继续写<深入理解java虚拟机>的对象创建的理解.这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录. (此文中所阐述的内容都是以Hot ...

  6. 浅谈Java虚拟机内存中的对象创建,内存布局,访问定位

    参考于 深入理解Java虚拟机 这里介绍HotSpot虚拟机(自带的虚拟机) 1.对象的创建 对于程序员来说,创建对象的方法: User user1 = new User(); User user2 ...

  7. HotSpot虚拟机对象探秘(对象创建,对象内存布局,对象访问定位)

    以常用的HotSpot虚拟机和JAVA内存区域堆为例,探讨对象的创建,对象的内存布局以及对象的访问定位 一.对象的创建 1)类加载:虚拟机遇到一条new指令时,先检测这个指令的参数能否在常量池中定位到 ...

  8. Java对象的创建、内存布局和访问定位

    在Java运行时数据区中,我们知道了虚拟机内存的概况,本文介绍虚拟机内存中的数据的其它细节,如对象如何创建.如何布局以及如何访问. 基于实用的原则,这里以HotSpot虚拟机和常用的内存区域Java堆 ...

  9. Java对象创建的过程及对象的内存布局与访问定位

    这里以HotSpot为例,且所说的对象指普通的Java对象,不包括数组和Class对象等. 1.对象创建的过程 1.类加载.解析.初始化:虚拟机遇到new时先检查此指令的参数是否能在常量池中找到类的符 ...

随机推荐

  1. JAVA基础----java中E,T,?的区别?

    http://825635381.iteye.com/blog/2017650 遇到<A>,<B>,<K,V>等,是用到了java中的泛型. 一般使用<T&g ...

  2. Android的权限检查

    Application的权限: 可以在AndroidManifest.xml中用<permission>定义运行Application需要的权限. 用<uses-permission ...

  3. 常见Xcode参数设置错误

    错误1 dyld: Library not loaded: /System/Library/Frameworks/AdSupport.framework/AdSupport Referenced fr ...

  4. 重叠I/O模型

    一. 重叠I/O的概念当调用ReadFile和WriteFile时,如果最后一个参数lpOverlapped设置为NULL,那么线程就阻塞在这里,直到读写完指定的数据后,它们才返回.这样在读写大文件的 ...

  5. string的实现

    面试常常用到string类的实现,自己总结了一下: #pragma once #include <iostream> #include <cassert> #include & ...

  6. 最优雅,高效的javascript字符串拼接

    这种方式是es6的语法.使用键盘1左边的那个字符 `` 拼接, 再加上js自带的模板引擎拼接字符串非常快速.这东西也没什么高深的,看几个例子就懂了. console.log(`<xml> ...

  7. 第三章 Git使用入门

    我们都知道Linux和Android是开源的.Linux下的软件很多都不直接以二进制形式的安装包提供,而是直接提供了源代码,为了减少发行包的大小,用户须先下载源代码,在本机上编译并安装,使用make. ...

  8. 初学Spring

    Spring是当今最流行的框架,今天开始,dayday同学要正式开始学习Spring了,加油 以下是一个简单的应用Spring框架的java程序 src\dayday\HelloSpring.java ...

  9. SQL-数学、字符串、时间日期函数和类型转换

    --数学函数 --ABS绝对值,select ABS(-99)--ceiling取上限,select CEILING(4.5)--floor去下限select FLOOR(4.5)--power 几次 ...

  10. ios创建自定义控件必须具备的三个方法

    1.当用代码创建控件时调用 -(instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { ...