最近在写 Linux 程序的时候,碰到这样的问题:Log4Cxx 0.10.0 在 Linux 下退出程序时导致程序中断,即提示出现 segmentation fault 错误。
如果你用 gdb 调试,会提示如下信息:
Program received signal SIGSEGV, Segmentation fault. 0x02fa68f3 in apr_vformatter () from /usr/lib/libapr-1.so.0
原因是因为 Log4Cxx 在退出时有非法的资源释放。
有一个解决办法:可以在程序退出时,显示地关闭 Log4Cxx 对象。
I have the same problem if I use log4cxx.AsyncAppender. I fixed the problem by calling log4cxx::LogManager::shutdown(); before the end of the process. It's not very clean but it works.
log4cxx::LogManager::shutdown();
来清理 log4cxx,而不是让它自己来清理。
注意: 需要在程序所有可能退出的地方,设置这一语句 ,否则依然会有 segmentation fault 的问题。
最近都在写一些跨 Windows 和 Linux 平台的 C++ 代码,略有心得,整理成文,备忘一下。
Visual Studio 会自动在源代码里添加
#pragma once
这个指令,在 Linux Gcc 编译器下应该怎么样处理类似的情况呢?
维基百科上有一个权威说法:
http://en.wikipedia.org/wiki/Pragma_once
所以,在编写跨平台的 C++ 代码时,最好使用下面这种方式来获得跨平台的特性:
#pragma once #ifndef GRANDFATHER_H #define GRANDFATHER_H struct foo { int member; }; #endif /* GRANDFATHER_H */
Windows 下使用 __declspec(dllexport) 来标明一个动态链接库的函数接口,而在 Linux 下,则完全没有这个必要。
这个时候,如果动态链接库代码需要跨平台,应该怎么处理呢?
你可以用如下宏来进行区分:
#ifdef WIN32 #define EXPORT_XX __declspec(dllexport) #else #define EXPORT_XX #endif
源代码的编码格式,最好统一用 GBK 或是 UTF-8,以避免不同编码器之间转换造成的乱码。
Windows 里 include 一个头文件,你的大小写可以随便写。但是在 Linux 上,你得小心了,clsssa.h 你不能写成
#include "ClassA.h"
因为在 Linux 上,文件及目录名是大小写敏感的。
Windows 处理宽字符集与窄字符集采用的方法,是用宏定义来区分。比如说 TCHAR 在不同的工程下,属于不同的类型。
而在 Linux 下,则需要注意这些问题。为了让你的代码可移植性强。一定要少用 BOOL, TCHAR 这些类型,而要用原生态的 C/C++ 类型,比如说 bool, char 等。
默认情况下,Visual Studio 新建的 Win32 Console 工程,全是类似于这样的。
int _tmain(int argc, _TCHAR* argv[]) { }
这些代码,移植到 Linux 下面,就需要我们自己做一部分工作。
如果你在 Visual Studio 用的是 Unicode 编码的,则对应的 Linux 代码应该是另外一套。我则是用宏裁剪了两个主函数出来。如果你有什么更好的方法,可以跟我分享。
虽然你的程序可能依赖大量的跨平台库,这些库号称是跨平台的,但是很有可能在不同的平台的表现,会有所不同。例如,log4cxx 在 Linux 和 Windows 下就会有字符集设置的差别。
你在编写跨平台代码的时候,就应该特别小心这些库的细微差别,及时调整。
以上是一些跨平台 C/C++ 代码编写的经验,备忘一下,也希望对大家有用。
最近在 Linux 下使用 log4cxx 库,使用的 log4cxx 版本为 0.10.0,结果无法显示中文日志信息。
这可怎么办呢?我不可能把中文日志全部一行一行替换为英文的,这可是一个非常傻B的举动。
经过研究,终于知道需要经过如下步骤才能让 log4cxx 在 Linux 下正常显示中文日志:
$ locale
$ export LC_ALL="zh_CN.UTF-8"
如果你的系统提示说没有安装本字符集,则需要用命令进行安装:
$ sudo apt-get install language-pack-zh-hans
LoggerPtr logger;
log4cxx::PropertyConfigurator::configure("./log4cxx.properties");
logger= Logger::getLogger("test") ;
logger->info(("Start logging"));
setlocale(LC_ALL, "zh_CN.UTF-8");
关键是最后这一句 setlocale,要设置得和终端一样,都是 zh_CN.UTF-8。
这样,你的程序就可以用 log4cxx0.10.0 输出中文日志信息了。
今天在 Ubuntu 下使用 OpenCV 来读取一个视频,碰到了下述问题:
所有的测试视频在 Windows 下用 OpenCV2.2.0 是可以正常读取的,应该是 ffmpeg 安装不完全或是不正确。
[swscaler @ 0x94b3e80]No accelerated colorspace conversion found from yuv420p to bgr24.
这个问题应该是 OpenCV 在用 ffmpeg 解压 yuv420 数据时出错。
对于上述问题,可以采用下面的办法来解决:
另外:如果是在 Ubuntu-Server 版下安装 OpenCV,在安装 libhighgui-dev 的时候,需要安装很多和桌面相关的依赖包。
想弄一个 Java 内嵌网页服务器 ( Embedded Web Server ),这样,在我的 Java 应用程序里,可以直接提供网页服务,而不用再给用户配置一个网页服务器。对于轻量级的本地网页服务,这样就足够了!
在 Java 应用里内嵌一个网页服务器,Jetty 是一个很好的选择。以下是我在摸索过程中,总结的基本步骤:
首先从官方网站下载开发包,再解压到你喜欢的目录,Java 的东西就是方便呀!
注意,Jetty 还分好几种包,Jetty@eclipse、Jetty@codehaus 是比较常见的,内容应该差不多,应该只是镜像罢了。
Java 开发中使用第三方库的时候,就需要把该库添加到工程里。Jetty 的库文件,全部在解压目录的 lib 目录下,比如说,在我这里就是
D:\eclipse\jetty-hightide-8.0.4.v20111024\lib
目录。在 Eclipse 里新建一个 Java 工程,同时,在工程上点右键,弹出的菜单里,选择”Properties”,打开工程属性对话框,进行如下操作:
在 “Java Build Path” ==> “Libraries” 选项卡里,选择 “Add External JARs” 按钮,然后切换到 Jetty 目录里的 lib 目录,把所有的 jar 包都添加到工程里。
现在,我们可以用最简单的例子来启动一个 Jetty 内嵌网页服务器,但是这个代码,只可以获得一个 404 错误。不过没有关系,这起码可以证明 Jetty Server is Ready!
相比于上面这个 404 错误的例子,相信你会更喜欢下面这个 HelloWorld 例子:
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import java.io.IOException; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; public class HelloWorld extends AbstractHandler { public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); response.getWriter().println("<h1>Hello World</h1>"); } public static void main(String[] args) throws Exception { Server server = new Server(8080); server.setHandler(new HelloWorld()); server.start(); server.join(); } }
如果需要在 Java 工程里进行 Embeded Server 开发,也可以把 Jetty 的库目录包含到 CLASSPATH 环境变量里。事实上,只要能找到下面几个文件就可以了:
servlet-api-3.0.jar jetty-util-8.0.4.v20111024.jar jetty-8.x.jar
手册上是这样写的,不过最后一个 jetty-8.x.jar 文件,我没有找到。
在调试 Visual Studio 2008 程序时,经常有一些动态链接库(即 dll 文件)需要加载到工程里,这样才能依赖第三方库进行程序调试。
这些动态链接库,往往都是测试版本或是开发中的版本,或者会有若干个版本;这个时候,如果直接把 dll 所在目录加到 PATH 里,则会有潜在冲突的危险;如果直接拷贝到 Visual Studio 的目录下,假如测试工程太多,每次有新版本的动态链接库更新时,你需要更新若干次,拷贝、粘贴苦不堪言。
在开发过程中,究竟怎样来让 Visual Studio 链接这些 lib 及 dll 文件会比较好呢?
总体上来说,有几种方法可以改变 Visual Studio 的环境变量设置:
这个方法最简单,也最直接,但是坏处是会影响全局的 PATH 设置,尤其是你包含着大量测试用的 dll 时。
通过 Visual Studio 菜单 ==> 工具 ==> 选项 ==> 项目和解决方案 ==> VC++目录,在下拉框里选择”可执行文件”,然后把 dll 所在路径添加进去。
这个方法也很简单,但是当你有若干个工程时,你每次更新 SDK 及其 dll 文件,你就要把所有的工程都更新,这个不符合文件唯一性的工程性准则。
在 Visual Studio ==> Project ==> Properties ==> Select Configuration ==> Configuration Properties ==> Debugging ==> Working directory 里填上 dll 所在目录,这样当在调试程序时,Visual Studio 会把当前工作目录切换到这个目录下,从而会自动读取本目录下的 dll 文件。
这个方法的优点很明显,简单!副作用也很明显,在你切换了当前工作目录后,你可能会找不到程序的配置文件,在程序里写的诸如”./config.ini”全部都找不到了;另外,你要把所有的 dll 都放到这个工作目录里,否则一样会提示说找不到 xxx.dll 的问题。
MSDN 上也有类似的介绍:How to: Set Environment Variables for Projects,方法很简单,在 “工程属性” ==> “调试” ==> “环境”里,添加类似如下所示的内容:
PATH=%PATH%;$(TargetDir)\DLLS
这样就可以把 $(TargetDir)\DLLS 临时添加到该工程所属的系统 PATH 里。
大家可以根据项目的实际情况,灵活选用以上方法。
注:本文撰写时参考了 StackOverflow 上的讨论话题:How do I set a path in visual studio?
在 Emacs Org Mode 里,默认情况下 _ 这种字符会被当成标记语言来进行转义。有的时候,如果你只是写写文章,这种默认的转义,会让你很不方便,尤其是你在写一篇介绍技术的文章,里面出现的变量名有很多的下划线。这时,你会说:”Oh, my god! 难道要我一个一个去标记不让 _ 转义么?”
如何设置让 Org Mode 在默认情况下,不转义 _ 字符呢?
你可以在一个 org 文件的开头,用下面的设置来关闭这个功能。
#+OPTIONS: ^:nil
如果你需要更方便的设置,可以把上面这个改为
#+OPTIONS: ^:{}
这样,当你写
a_{b}
时,_ 后被 {} 括起来的内容就会被转义,而写 a_b 时,就按普通的方式来显示。
如果你是用 org sites 来写笔记,想让某个 site 的所有 org 文件不转义 _ 字符,则也可以直接在 org sites 配置文件里,配置下面这么一句,一了百了:
(setq org-export-with-sub-superscripts nil)
也可以在 org sites 里设置这个属性,只作用于某一个 site:
:sub-superscript nil
如果需要像上面一样,采用相对智能的 {} 方式,可以设置成
:sub-superscript {}
或是直接用 elisp 来设置全局的属性:
(setq org-export-with-sub-superscripts '{})
这样就会用 {} 来转义了。
最近折腾得够呛,天天写代码,都没时间更新博客了。
总结一下最近碰到的两个问题,写到中文 Wiki 里了,希望对大家有点帮助。
最近要迁移大把工作成果到 Linux 平台,即要在 Linux 下大量地折腾与 OpenCV 相关的代码。很久没有在 Linux 上折腾了,时过境迁,同样碰到了不少问题,写成日志《OpenCV 各种安装错误汇总》。
在 Visual Studio 2008 MFC 工程中,利用 Berkeley DB 来构建数据存储引擎时,在编译 db.h 文件时出现编译错误,错误提示内容如下:
错误 3 error C2143: 语法错误 : 缺少"}"(在"("的前面) e:\water\berkeleydb\include\db.h 1226
微软的 MSDN 上有对 error C2143 的编译器错误进行解释,不过基本上没有太多可读性、可借鉴性,大意应该是一些宏定义、命名出错等。
最后,还是通过万能的 Google 大神找到了解答方法。错误的原因是 DB_TYPE, DB_UNKNOWN 类型已经在 MFC 系统头文件中被定义过,解决办法之一是在 db.h 中定义 DB_TYPE, DB_UNKNOWN 的语句之前加上如下语句即可:
#ifdef DB_UNKNOWN #undef DB_UNKNOWN #endif #ifdef DBTYPE #undef DBTYPE #else #define DBTYPE BDBTYPE #endif
看来,C 和 C++ 混在一块,命名、类型定义真是一个大问题。以后碰到类似的问题,也可以采用类似的解决办法。
参考资料:
今天为这件事情折腾了一天!
我用 Java 写的中间件,会根据用户的需要,启动一个子进程,这个进程是用 C++ 写的,正好用到了 Log4Cxx 来记录日志以便分析。这个进程,单独从控制终端运行,没有任何问题;但是一旦用 Java 启动子进程的方式来启动,则这个子进程刚一启动就阻塞不动了,然后大概等上几十分钟到两个小时不等,该子进程则又能顺利执行下去。
第一次碰到这样的问题,很怪异,经过反复测试及万能的 Google 帮忙,找到Java调用外部程序挂起原因,Log4Cxx 原来是罪魁祸首,因为它重定向了大量的日志信息到控制台,由于 Java 进程没有清空程序写到缓冲区的内容,结果导致程序一直在等待。
解决这个问题有两个办法:
问题搞定了,世界又变得很美好了!