Elton's Blog

C/C++

通过JNI实现Java对C/C++的调用

by on 七.29, 2011, under C/C++, Java, Linux

JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

大致步骤

  1. 编写带有native声明的方法的java类
  2. 使用javac命令编译所编写的java类
  3. 使用javah命令生成扩展名为h的头文件
  4. 使用C/C++实现本地方法
  5. 将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访问到了,不用每次指定环境变量了。

5 Comments :, , more...

让Redis使用TCMalloc,实现高性能NOSql服务器

by 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"
3 Comments :, more...

ubuntu 11.04安装OpenCV

by 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运行正常

Leave a Comment : more...

ubuntu 11.04安装gcc 4.6.1

by 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
10 Comments :, more...

register、volatile、restrict 三关键字的用法

by 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&lt;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博客.

Leave a Comment :, , , more...



Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit my friends!

A few highly recommended friends...