笔误,是福是祸?(linux so搜索路径,SWIG等)

18 篇文章 0 订阅
18 篇文章 0 订阅

        这是一篇非常有聊(?)的日记。

        昨晚心血来潮,在尝试用SWIG(http://www.swig.org)给C++的库做Python绑定,网上例子非常多,看了几个就动手。后来被动态链接的问题折腾到晚上十二点半,对我这个每天早上7点前起床的人来说有点伤不起。而且,你知道吗,错误原因是我把函数名写错了,测试用源码里的randnum在接口里我写成了rand_num。

        SWIG的工作原理是:你提供源代码,然后把源码里的部分声明抽出来,写成一个 扩展名.i 的文件提供给SWIG,再指定你的源码语言(比如C++)和目标语言(比如Python),SWIG会帮你生成一个长长的胶水源码,这个胶水代码就是python和C++的桥梁了。

        编译的时候比较复杂,我直接说目的以便理清思路:最终你会得到两个动态链接库,一个是胶水库,一个是实际干活的库。胶水库是用胶水代码生成的,可以被python识别,而且里面没有具体函数实现。实际干活的库就是你的源码编译成的so文件,它和python、SWIG一点关系都没有是纯正的C/C++库,它会被胶水库调用。此外为了做一些管理工作还会生成一个xxxx.py 源码,写python代码的时候直接import xxxx就可以了。

        既然有两个so文件,这就涉及到so搜索的问题。敢写这篇日记说明我已经基本搞清了:)。

        首先,python有自己的导入dll/so扩展库的机制,可以通过python标准库的imp模块的imp.load_module实现,参数里可以指定路径。胶水库通过这种方式导入,这个很好理解。

        其次,在导入胶水dll/so的时候,会导入相应依赖的dll/so,这个导入是操作系统级别的,也就是是C/C++领域的问题,和python没有关系。这个导入能否成功取决于两点:在搜索路径下能搜索到相应文件;这个文件里能找到所有需要的符号定义(函数实现),如果有未定义的符号会报错(至少运行时肯定会报错,这个地方表述不是完全准确,建议通读《程序员的自我修养——链接、装载与库》的前半部分。我不想误人子弟。)

        既然这个导入取决于两个关键点,那么这两个关键点都有可能出错。

        1、搜索路径问题。windows和linux的动态链接库,除了命名方式不同,还有个很大区别是搜索方式不同。windows默认先搜索当前目录,linux默认不搜索当前目录。具体顺序挺麻烦,请百度。在windows下只要把dll拷贝到当前工作目录就能用了(windows有个麻烦的事情,是注意两个so的命名不要一样了,可能造成冲突)。

            linux建议这么做:执行shell命令export LD_LIBRARY_PATH=“工作目录”。这样可以临时的把当前目录加进来,而且不影响其他进程。如果你的库已经稳定了,就把你的so拷贝到/usr/local/lib或者类似的地方,它们是默认的so搜索路径。

        2、动态链接库里的定义是否完整。为啥会不完整捏?举我犯过的这个脑残的例子。在胶水so里,我声明了一个函数rand_seed,在实际so里,没有rand_seed函数,只有randseed函数,而且这个错误在复杂的编译过程中都不会被发现,只有在动态加载这个工作so时才会发现这个错误!由于加载工作so的时候你已经在python环境里了,错误提示会让你很纳闷。

        一开始我猜测还有一种找不到函数定义的情况:C++对函数名做了修饰(在非入门书籍上应该都有提到),但是我后来发现python会去找 _Zrand_numV_类似这种样子的符号,也就是说swig把这些问题都处理妥当了,不用担心。


        做这种麻烦的事情,关键是理清思路,如果照着教程顺利走下来,真是没有什么收获,而且遇到复杂错误会茫然不知所措。基础知识是王道:)

附很好的一篇SWIG,C++ Python 入门教程:

http://hi.baidu.com/125725385/blog/item/8811a511d3cbae07213f2e97.html


———————————————一觉醒来,对上一节一些错误的修正—————————————————————

        仔细看了一天SWIG,纳闷为啥要有两个动态链接库,放在一起岂不是更方便。仔细读了官方文档,果不其然。

        http://www.swig.org/Doc1.3/SWIGDocumentation.html#Python

        其中的:31.2.2  Using distutils,其方法就是写setup.py的时候,把俩库链接到一起,这样使用起来就没有上面那种方法找不到so的麻烦了。恩。但是按照本日记的核心思想,只有给自己找麻烦才能进步。

        又学到一招神技:如果编译一个liba.so时引用到另一个libb.so,那么可以把b静态链接到a里,变成一个so,发布给别人的时候可能方便一些。大致写法如下,主要是用到gcc的 -Xlinker 参数。       

$ gcc -shared example.o example_wrap.o -L/home/beazley/projects/lib -lfoo \
      -Xlinker -rpath /home/beazley/projects/lib  \
      -o _example.so

———————————————————————————————————————————————————


        几个月前,一位同事给我们的游戏项目GUI部分做控件扩展。我们的GUI界面都是用xml文件配置的,支持xml包含,类似C语言的include。他忙活了一天发现了一个诡异的xml包含时错误,百试不得其解。他给我看了这个问题,我看过配置也很头疼。后来我俩分工,我调试源码,他查配置,俩人折腾了一个多小时。最后就在我快要搞清xml包含机制的时候,他发现了问题:单词拼错了。

        我感谢他的这次拼写错误,如果没有这次拼写错误,xml读取机制在我脑海中一直是个“谜团”(虽然现在仍然是小谜团)。顺带说一句,项目整个的GUI库包括独家xml库都是一位大侠自己写的。


        思绪飘到了去年夏天,一位大侠扩展了脚本引擎代码,然后发生诡异的编译时异常。我查了一周多的时间,发现是扩展指令集的时候,优化用数组忘记扩展了,导致越界。我想说的是,在这个过程中,我用到了内存泄漏检查工具(原理是重载new进行记录),了解了各种VC++配置,知道了5种神奇的Debug技巧,生平第一次靠汇编代码调试程序。最后,这个可爱的BUG引领我走进了《编译原理》的大门……至少是完全了解了词法分析(至今停留在语法分析那章)。


        我想说的是,一个有经验的程序员肯定有过不少类似的经历。对一个半吊子程序员来说尚且如此,更不用谈各种高手的成长经历了。其实对任何脑力劳动者,比如数学家、科学家可能都免不了这些经历。

        总结一下,让自己记住:遇到任何小问题,不要轻易放过,更不要一开始就尝试绕过去。正面和它死磕,有极大可能性你会揪出一只大象。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值