C/C++
通过JNI实现Java对C/C++的调用
by Elton on 七.29, 2011, under C/C++, Java, Linux
JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
大致步骤
- 编写带有native声明的方法的java类
- 使用javac命令编译所编写的java类
- 使用javah命令生成扩展名为h的头文件
- 使用C/C++实现本地方法
- 将C/C++编写的文件生成动态连接库
1) 编写java程序:
1 2 3 4 5 6 7 8 9 10 11 12 | public class HelloNative{ public native void greeting();//所有native所修饰的都是本地方法 static{ System.loadLibrary("HelloNative");//载入本地库 } public static void main(String[] args){ new HelloNative().greeting(); // System.out.println(System.getProperty("java.library.path")); } } |
声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明该方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。 Load动态库:System.loadLibrary(“HelloNative”);加载动态库(我们可以这样理解:我们的方法 greeting()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“HelloNative”是动态库的名字。
2) 编译
1 | javac HelloNative.java |
3) 生成扩展名为h的头文件
1 | javah HelloNative |
命令执行后会在当前目录下生产一个c的头文件,名字为HelloNative.h。内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class HelloNative */ #ifndef _Included_HelloNative #define _Included_HelloNative #ifdef __cplusplus extern "C" { #endif /* * Class: HelloNative * Method: greeting * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloNative_greeting (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif |
这个h文件相当于我们在java里面的接口,这里声明了一个 Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致
4) 编写本地方法实现和由javah命令生成的头文件里面声明的方法名相同的方法。
1 2 3 4 5 6 7 8 9 | #include <stdio.h> #include <jni.h> #include "HelloNative.h" JNIEXPORT void JNICALL Java_HelloNative_greeting (JNIEnv * env, jobject obj) { printf("Hello, Native!\n"); } |
5) 生成动态库
1 | gcc -fPIC -I/home/elton/jdk/include -I/home/elton/jdk/include/linux -shared -o libHelloNative.so HelloNative.c |
注意,必须告知编译器jni.h所在的接口位置。linux的动态库都是以lib开头,以.so结尾的,要遵守这个命名规范。-fPIC是告诉编译器生成跟位置无关的动态链接库
命令执行后,会在当前目录生成一个libHelloNative.so的动态链接库文件
6) 运行程序
1 | java -Djava.library.path=. HelloNative |
必须指定java.library.path变量的内容,告诉java你的动态链接库的位置。
或者在命令行上输入
1 | export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH |
这样就不用每次调试的时候都输入-Djava.library.path=.这个参数了。
当你部署的时候,你通过System.out.println(System.getProperty(“java.library.path”));得到你系统的java.library.path位置,然后把你的动态链接库拷贝到这个目录中。我用的是ubuntu 11.04 64位版本,得到的结果是
1 | /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib |
这样你的动态链接库就永远都会被java访问到了,不用每次指定环境变量了。
让Redis使用TCMalloc,实现高性能NOSql服务器
by Elton on 七.28, 2011, under C/C++, Linux, NoSQL
TCMalloc(Thread-Caching Malloc)是google开发的开源工具──“google-perftools”中的成员。与标准的glibc库的malloc相比,TCMalloc在内存的分配上效率和速度要高得多,可以在很大程度上提高MySQL服务器在高并发情况下的性能,降低系统负载。
TCMalloc库的安装步骤(Linux环境):
Step 1. 64位操作系统请先安装libunwind库(32位操作系统不要安装)
libunwind库为基于64位CPU和操作系统的程序提供了基本的堆栈辗转开解功能,其中包括用于输出堆栈跟踪的API、用于以编程方式辗转开解堆栈的API以及支持C++异常处理机制的API。
1 2 3 4 5 6 | wget http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-alpha.tar.gz tar zxvf libunwind-0.99-alpha.tar.gz cd libunwind-0.99-alpha/ CFLAGS=-fPIC ./configure make CFLAGS=-fPIC make CFLAGS=-fPIC install |
Step 2、安装google-perftools:
1 2 3 4 5 6 7 8 | wget http://google-perftools.googlecode.com/files/google-perftools-1.8.1.tar.gz tar zxvf google-perftools-1.8.1.tar.gz cd google-perftools-1.8.1/ ./configure --disable-cpu-profiler --disable-heap-profiler --disable-heap-checker --disable-debugalloc --enable-minimal make && make install sudo echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf #如果没有这个文件,自己建一个 sudo /sbin/ldconfig |
Step 3. 安装Redis
1 2 3 4 5 | $ curl -O http://redis.googlecode.com/files/redis-2.2.12.tar.gz $ tar xzvf redis-2.2.12.tar.gz $ cd redis-2.2.12 $ make USE_TCMALLOC=yes $ sudo make install |
Step 4. 检查tcmalloc是否生效
1 2 | sudo lsof -n | grep tcmalloc redis-ser 31590 elton mem REG 8,3 1155539 4856411 /usr/local/lib/libtcmalloc_minimal.so.0.2.1 |
Step 5. 测试Redis
1 2 3 4 5 6 7 8 9 10 11 12 | # 修改配置文件: vim redis.conf # 找到 daemonize,将后面的no改为yes,让其可以以服务方式运行 # 然后启动 redis: $ ./redis-server ./redis.conf #连接数据库进行测试 $ src/redis-cli redis> set foo bar OK redis> get foo "bar" |
ubuntu 11.04安装OpenCV
by Elton on 七.27, 2011, under C/C++, Linux
Step 1:安装必要的依赖包
1 | sudo apt-get install build-essential libgtk2.0-dev libjpeg62-dev libtiff4-dev libjasper-dev libopenexr-dev cmake python-dev python-numpy libtbb-dev libeigen2-dev yasm libfaac-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev |
Step 2:安装ffmpeg
1 2 3 4 5 6 7 | cd ~ wget http://ffmpeg.org/releases/ffmpeg-0.7-rc1.tar.gz tar -xvzf ffmpeg-0.7-rc1.tar.gz cd ffmpeg-0.7-rc1 ./configure --enable-gpl --enable-version3 --enable-nonfree --enable-postproc --enable-libfaac --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libtheora --enable-libvorbis --enable-libxvid --enable-x11grab --enable-swscale --enable-shared make sudo make install |
Step 3:安装OpenCV 2.3
1 2 3 4 5 6 7 8 | cd ~ wget http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.3/OpenCV-2.3.0.tar.bz2 tar -xvf OpenCV-2.3.0.tar.bz2 cd OpenCV-2.3.0/ mkdir release cd releasecmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_TBB=ON -D WITH_V4L=OFF -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON .. make sudo make intall |
Step 4:添加库的路径
1 | sudo vim /etc/ld.so.conf.d/opencv.conf |
加入:
1 | /usr/local/lib |
更新系统库
1 | $sudo ldconfig |
设置环境变量
1 | $sudo vim /etc/environment |
加入
1 | PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig |
至此,OpenCV的设置都完成了。
Step 5:测试
进入OpenCV源代码目录中的sample
1 2 3 | cd ~/OpenCV-2.3.0/samples/c chmod +x build_all.sh ./build_all.sh |
然后执行
1 | ./facedetect --cascade="/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" --scale=1.5 lena.jpg |
你应该可以看到以下图片,说明OpenCV运行正常

ubuntu 11.04安装gcc 4.6.1
by Elton on 七.27, 2011, under C/C++, Linux
首先下载相应的源代码:
ftp://ftp.dti.ad.jp/pub/lang/gcc/releases/gcc-4.6.1/
#下载 gcc-4.6.1.tar.bz2
ftp://ftp.dti.ad.jp/pub/lang/gcc/infrastructure/
#下载 gmp-4.3.2.tar.bz2 mpfr-2.4.2.tar.bz2 mpc-0.8.1.tar.gz
Step 0:
1 2 3 | $sudo apt-get install build-essential $sudo apt-get install zlibc $sudo apt-get install zlib1g-dev |
Step 1: 安装 gmp-4.3.2
1 2 3 4 | #cd to src_dir $./configure --prefix=/usr/local/gmp-4.3.2 $make $sudo make install |
Step 2: 安装 mpfr-2.4.2
1 2 3 4 | #cd to src_dir $./configure --prefix=/usr/local/mpfr-2.4.2 --with-gmp=/usr/local/gmp-4.3.2 $make $sudo make install |
Step 3: 安装 mpc-0.8.1
1 2 3 4 | #cd to src_dir $./configure --prefix=/usr/local/mpc-0.8.1 --with-gmp=/usr/local/gmp-4.3.2 --with-mpfr=/usr/local/mpfr-2.4.2 $make $sudo make install |
Step 4: 安装 gcc-4.6.0
1 2 3 4 5 | $export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/mpc-0.8.1/lib:/usr/local/gmp-4.3.2/lib:/usr/local/mpfr-2.4.2/lib #cd to src_dir $./configure --prefix=/usr/local/gcc-4.6.1 --enable-threads=posix --disable-checking --disable-multilib --enable-languages=c,c++ --with-gmp=/usr/local/gmp-4.3.2 --with-mpfr=/usr/local/mpfr-2.4.2 --with-mpc=/usr/local/mpc-0.8.1 $make $sudo make install |
Step 5: 多版本支持
为了让ubuntu支持多个gcc版本,需要做以下设置:
1 2 3 4 | $ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.5 40 $ sudo update-alternatives --install /usr/bin/gcc gcc /usr/local/gcc-4.6.1/bin/gcc 60 #选择你需要的版本 $sudo update-alternatives --config gcc |
Step 6:添加新版共享库
为了在编译软件时候,可以使用到最新的共享库
1 | $sudo vim /etc/ld.so.conf.d/x86_64-linux-gnu.conf |
添加下面内容
/usr/local/gcc-4.6.1/lib64/
保存后执行,更新共享库
1 | $sudo ldconfig |
register、volatile、restrict 三关键字的用法
by Elton on 七.27, 2011, under C/C++
register
使用修饰符register声明的变量属于寄存器存储类型。该类型与自动存储类型相似,具有自动存储时期、代码块作用域和内连接。声明为register仅仅是一个请求,因此该变量仍然可能是普通的自动变量。无论哪种情况,用register修饰的变量都无法获取地址。如果没有被初始化,它的值是未定的。
volatile
volatile告诉编译器该被变量除了可被程序修改外,还可能被其他代理、线程修改。因此,当使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,而不使用寄存器中的缓存的值。比如,
1 2 | val1=x; val2=x; |
如果没有声明volatile,系统在给val2赋值的时候可能直接从寄存器读取x,而不是从内存的初始位置读取。那么在两次赋值之间,x完全有可能被被某些编译器未知的因素更改(比如:操作系统、硬件或者其它线程等)。如果声明为volatile,编译器将不使用缓存,而是每次都从内存重新读取x。
restrict
restrict是c99引入的,它只可以用于限定指针,并表明指针是访问一个数据对象的唯一且初始的方式,考虑下面的例子:
1 2 3 | int ar[10]; int * restrict restar=(int *)malloc(10*sizeof(int)); int *par=ar; |
这里说明restar是访问由malloc()分配的内存的唯一且初始的方式。par就不是了。那么:
1 2 3 4 5 6 7 8 | for(n=0;n<10;n++) { par[n]+=5; restar[n]+=5; ar[n]*=2; par[n]+=3; restar[n]+=3; } |
因为restar是访问分配的内存的唯一且初始的方式,那么编译器可以将上述对restar的操作进行优化:restar[n]+=8;。而par并不是访问数组ar的唯一方式,因此并不能进行下面的优化:par[n]+=8;。因为在par[n]+=3前,ar[n]*=2进行了改变。使用了关键字restric,编译器就可以放心地进行优化了。这个关键字据说来源于古老的FORTRAN。
总结
两个关键字:volatile和restrict,两者都是为了方便编译器的优化。
转载自:register、volatile、restrict 三关键字的用法 – RaymondAmos的技术专栏 – CSDN博客.


