Project RC

一种标准阿西的设计与实现。

OS X 上在 Java 项目中通过 JNI 调用 C/C++ 动态链接库

创建于
分类:Dev
标签:JavaJNI

1. 创建 Java 代理类

首先在 Java 项目中创建一个 DemoProxy 类,作为 Java 调用 C/C++ 动态库的代理类,内容如下:

package com.demo;

public class DemoProxy {
    static {
        System.loadLibrary("demo");
    }
    public static native double square(double a);
}

这段代码首先在静态代码块中加载动态库 demo,然后声明一个静态方法 double square(double a) 并用 native 关键字修饰,表明是一个 native 函数,在动态库中实现。

接着在终端中进入项目主目录,执行:

javac src/com/demo/DemoProxy.java -d ./bin

编译生成的 class 文件被放在了主目录下的 bin 目录(若没有需先创建)。

然后执行:

javah -classpath ./bin com.demo.DemoProxy

classpath 后的 ./bin 指定了命令在 bin 目录下寻找类文件,注意 com.demo.DemoProxy 的包名不能少。这会在项目主目录生成一个 com_demo_DemoProxy.h 文件,可以看到内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_demo_DemoProxy */

#ifndef _Included_com_demo_DemoProxy
#define _Included_com_demo_DemoProxy
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_demo_DemoProxy
 * Method:    square
 * Signature: (D)D
 */
JNIEXPORT jdouble JNICALL Java_com_demo_DemoProxy_square
  (JNIEnv *, jclass, jdouble);

#ifdef __cplusplus
}
#endif
#endif

这里的 JNIEXPORT jdouble JNICALL Java_com_demo_DemoProxy_square (JNIEnv *, jclass, jdouble); 也就是我们将来所需调用的函数的声明。

最顶部 #include <jni.h> 包括了 jni.h 头文件,但是在 OS X 系统上,改成 #include <JavaVM/jni.h> 比较好,后面编译的时候就不需要指定头文件目录了。

2. 实现 C/C++ 函数

这里简便起见,创建 test.htest.c 两个文件,都放在项目的 src 目录,分别如下:

//test.h

#ifndef __TEST__
#define __TEST__

double square(double a);

#endif
//test.c

#include "test.h"

double square(double a) {
    return a * a;
}

实现了一个计算平方的函数。

3. 实现 Java 和 C/C++ 的连接

JNI 事实上是无法直接调用之前已经写好的 C/C++ 函数的,需要另外实现函数来调用现存的 C/C++ 函数,也就是需要实现先前自动生成的 .h 头文件中声明的函数。这一步中实现的函数起到连接 Java 代码和现存的 C/C++ 代码的作用。

创建一个 demo_proxy.c 文件,放在 src 目录中,包括一下之前生成的头文件,并实现相应函数,代码如下:

#include "../com_demo_DemoProxy.h"
#include "test.h"

JNIEXPORT jdouble JNICALL Java_com_demo_DemoProxy_square (JNIEnv *env, jclass cls, jdouble d) {
    return square(d);
}

这里 jdoublejclass 等类型是 Java 数据类型在 C 的对应的映射,具体看这里:JNI 数据类型与 Java 数据类型的映射关系

然后编译所有的 C 文件,在 src 目录中执行:

gcc -dynamiclib -o ~/Library/Java/Extensions/libdemo.jnilib *.c

如果是 C++ 文件就用 g++ 命令。这里的命令将刚才写的 C 函数封装到了一个动态链接库中,注意生成的动态库的名字和后缀:libdemo.jnilib,OS X 下 JNI 调用的动态库后缀应该是 jnilib,动态库名字最前面要加 lib

4. Java 中调用代理类的函数

这时候不出意外的话,一开始的 Java 项目中就可以通过 DemoProxy 类的静态方法来调用 test.c 中的 C 函数了,如下:

package com.demo;

public class Main {
    public static void main(String[] args) {
        System.out.println(DemoProxy.square(3.0));
    }
}

这样就大功告成了!

5. 参考资料

OS X 上在 Java 项目中通过 JNI 调用 C/C++ 动态链接库

在 CentOS 上搭建 Shadowsocks 和 LNMP 过程及遇到的问题

创建于
分类:Ops
标签:LinuxShadowsocks

这几天闲着蛋疼,折腾了一下 VPS。这玩意每次折腾都得烦死,但每次都忍不住地去折腾。

1. 过程

其实过程没什么好写的,网上到处都是教程。VPS 我用的 ConoHa 新加坡 1GB 内存 CentOS 6.6 32bit,下面是我参考的内容。

1.1 Shadowsocks

1.2 LNMP

2. 问题

安装 Shadowsocks 倒是没遇到什么问题,比较坑的主要是在安装 LNMP 之后,大致有以下几个问题。

2.1 LNMP 内存占用高

一开始系统选了 CentOS 7.1 x64,安装 LNMP 的时候图新鲜,MySQL 和 PHP 都选了最新的版本,结果最后 1GB 内存占用了 800MB(那时候都还没装 Shadowsocks)。其实 LNMP 官方文档的系统需求写得很清楚了:

128M 以上内存,Xen 的需要有 SWAP,OpenVZ 的另外至少要有 128MB 以上的 vSWAP 或突发内存(小内存请勿使用 64 位系统),MySQL 5.6 及 MariaDB 10 必须 1G 以上内存

2.2 给非 root 用户添加 sudo 权限

一般肯定得新建一个用户,直接用 root 会比较危险,比如我们新建了一个叫 user 的用户,它所在的用户组叫也叫 user。user 用户默认是不可以用 sudo 的,所以先切到 root 用户,cd /etcls -l 之后可以找到一个叫 sudoers 的文件,这文件很牛逼,注意它权限是 -r--r-----,root 用户都只能读不能写,但这不妨碍我们去改变它的权限,chmod u+w sudoers 即可给它添加 root 用户对它的写权限,vim sudoers 打开,然后找到 root ALL=(ALL) ALL,在下面添加一行 user ALL=(ALL) ALL:wq 保存并退出,别忘了 chmod u-w sudoers 把 root 用户的写权限去掉。这样再切回 user 用户就可以用 sudo 了。

2.3 文件权限

Linux 的文件权限一直没搞懂,这次这么一折腾似乎稍微明白了,说实话并不确定,如果我的理解没错的话,是这样的:

正如上一个问题里面出现的,ls -l 之后可以看到文件的权限,形如 -r--r-----。这东西有 10 位,第一位表示文件类型,比如这里 - 表示文件,如果是 d 则表示目录,之后的 9 位分为三段,每段 3 位,依次代表「所属用户」「所属组」「其他用户」对它的权限,3 位分别是 rwx,依次表示「读」「写」「执行」权限。权限后面可看到文件的「所属用户」和「所属组」。

比如 -r--r----- 1 root root 4024 Jul 18 19:13 sudoers,这个文件所属用户是 root,所属组是 root,前面的权限表示:root 用户可以对它进行读操作,root 组的其他用户也可以对它进行读操作,非 root 组的用户不可以对它进行任何操作,如果你在 user 用户下试图用 vim 打开它会提示「Permission denied」。这个权限也可以表示为 440,计算方法是这样的:「读」是 4、「写」是 2、「执行」是 1,「无权限」是 0,加起来就行,这样只需要 3 个 0~7 的数字即可表示权限,如下表:

数字 权限
0 无权限
1 x
2 w
3 wx
4 r
5 rx
6 rw
7 rwx

这样要对 sudoers 文件添加 root 用户对它的写权限就可以执行这个命令 chmod 640 sudoers,而上一个问题中所用的是这个命令 chmod u+w sudoers,当然也是 OK 的,u+w 中 3 个字符连起来表示给文件「所属用户」「添加」「写权限」,规则如下表:

字符 含义
a 所有权限/所有用户
u 用户
g 用户组
o 其他用户
字符 含义
+ 添加(权限)
- 去除(权限)
= 等于(权限)

2.4 git clone 失败

可能因为当前用户对当前目录没有写权限,我当时用 user 用户在 /home/wwwroot(即 nginx 默认的网站目录 default 所在的那个文件夹)试图 git clone,提示没权限,折腾了半天以为因为 GitHub 那边公钥没填好,后来才发现 wwwroot 目录是 root:root 的,且权限为 drwxr-xr-x,也就是说非 root 用户只能读和执行,不能写,即使是 root 组的其他用户。最后怎么解决的在后面的问题里面讲。

2.5 vhost 创建虚拟主机之后 403 Forbidden

这个问题归根到底也是文件权限问题,我是先在 user 用户下 git clone 了一个目录在 user 主目录,假设目录名为 test,属主和属组都是 user,sudo lnmp vhost add 之后,指定了 testtest.example.com 域名的主目录,命令自动把 test 目录及其内容文件的属主和属组都改成了 www,test 目录权限为 drwxr-xr-x,按理说 nginx 是可以访问它的,但是结果是 403 Forbidden,后来发现 user 主目录本身权限是 drwx------,这才导致 403。所以建议把虚拟主机主目录都放到 /home/wwwroot,这个目录虽然是 root:root 的,但权限是 drwxr-xr-x,www 用户可读。

2.6 user 用户无法更改虚拟主机目录中的文件

上一个问题中说到 sudo lnmp vhost add 之后,虚拟主机主目录是 www:www 的,权限为 drwxr-xr-x,所以 user 是没法更改内容的,但是平时为了安全起见都用 user 用户,改个网页什么的也 sudo 也太麻烦了吧,最后我的解决办法是,把 test 目录属主改为 user,属组保持原来 www 不变,用命令 chown user:www -R test,保持文件权限不变,即 drwxr-xr-x,这样 user 用户就可以自由读写,nginx 也可以读而不会导致 403。这样改的话,我猜想如果以后运行 PHP 需要读写文件肯定会权限不够,不过到时候把权限改成 775 应该就行了。

2.7 虚拟主机目录中的 .user.ini 文件删不掉

有时候某个虚拟主机不想要了,sudo lnmp vhost del 删了之后还没完,还得自己把目录删除,就拿上面的 test 目录为例,这里面有个 .user.ini 文件直接删是怎么都删不掉的,它属主和属组都是 root,但即使用 sudo rm .user.ini 还是删不掉。就在差点就准备把 VPS 删了不玩了的时候,去看了 lnmp 命令的脚本,vim /bin/lnmp,发现在创建虚拟主机的时候,建立 .user.ini 文件后,执行了 chmod 644 ${vhostdir}/.user.inichattr +i ${vhostdir}/.user.ini 这两个命令,查了一下,原来罪魁祸首是第二个命令,chattr 命令可以改变文件的属性而不是权限,这里的 +i 作用是「设定文件不能被删除、改名、设定链接关系,同时不能写入或新增内容」,所以即使是 root 用户也没办法删除它。这里 +-= 的含义和 chmod 时用到的含义相同,第二个字符规则如下表:

字符 含义
A 文件或目录的 atime (access time) 不可被修改 (modified), 可以有效预防例如手提电脑磁盘 I/O 错误的发生。
S 硬盘 I/O 同步选项,功能类似 sync。
a 即 append,设定该参数后,只能向文件中添加数据,而不能删除,多用于服务器日志文件安全,只有 root 才能设定这个属性。
c 即 compresse ,设定文件是否经压缩后再存储。读取时需要经过自动解压操作。
d 即 no dump,设定文件不能成为 dump 程序的备份目标。
i 设定文件不能被删除、改名、设定链接关系,同时不能写入或新增内容。i 参数对于文件 系统的安全设置有很大帮助。
j 即 journal,设定此参数使得当通过 mount 参数:data=ordered 或者 data=writeback 挂载的文件系统,文件在写入时会先被记录(在journal中)。如果 filesystem 被设定参数为 data=journal,则该参数自动失效。
s 保密性地删除文件或目录,即硬盘空间被全部收回。
u 与s相反,当设定为u时,数据内容其实还存在磁盘中,可以用于undeletion。

对于这个 .user.ini 文件,只要 sudo chattr -i .user.ini 一下就能删了。用 lsattr 命令可以查看相应文件的属性。

3. 结尾

就这么多了,以后每折腾一阶段都要去备份一下 VPS,这玩意出问题太麻烦了。

关于 chmodchownchattr 的更多资料,见维基百科:chattr chmod chown

4. 参考资料

在 CentOS 上搭建 Shadowsocks 和 LNMP 过程及遇到的问题

树莓派上手三天总结

创建于
分类:Fun
标签:Raspberry PiLinux

7 月 25 日,伴随着一点冲动,就下单买了最新的树莓派 B+ 以及入门必备的一些配件,26 日下午送到,就开始了这三天的折腾。

1. 启动

首先要启动,到 树莓派官网 下载最新的固件,我下载的是 Raspbian June 2014 版本,下载好是 zip 文件,解压得到 img 镜像文件。然后要把 img 文件写入到 SD 卡里面。方法如下:

  • 在 Windows 上可以用一个叫 Win32DiskImager 的软件(这里下载),这个软件用起来超简单,只需要选好 img 文件和要写入的 SD 卡盘符,点「Write」等它写入完就可以了。
  • 在 OS X 上比 Windows 要麻烦一点,先用 df -h 命令查看当前挂载的卷,找到 SD 卡的分区对应的设备文件,如 /dev/disk1s1,然后用 diskutil unmount /dev/disk1s1 将这些分区卸载,再执行 diskutil list,记住 SD 卡对应的设备文件,如 /dev/disk1,然后用 dd bs=4m if=2014-06-20-wheezy-raspbian.img of=/dev/rdisk1 来写入镜像文件,当然 if 和 of 指向的地址都要改成你自己的,这个命令执行的时候没有提示,就卡住不动,一定要耐心等待,写入好了之后就会提示你。

于是装有 Raspbian 的 SD 卡就制作完成了,插到树莓派里,插上网线,接上电源,树莓派就会开机,如果有显示器,就从 HDMI 接口接过去就有显示了,不过据说如果要 HDMI 转 VGA 的话,千万不能用没有自带电源的转换线,一定要用自带电源接口的。当然我这里没有 HDMI 转 VGA 线,也没有 HDMI 接口的显示器,这种情况下要想连接到树莓派可以通过 SSH。

2. 远程登录

Raspbian 系统默认是开启 SSH 的,也就是说它一开机你就可以用 SSH 连接它,SSH 需要提供系统的用户名和密码,Raspbian 默认用户名是 pi,密码是 raspberry。到路由器网页,一般默认是 192.168.1.1,找到树莓派分配到的 IP 地址,记下待用。

  • Windows 上连 SSH 可以用 PuTTY(官网下载),图形界面,操作简单,填上树莓派的 IP 地址,其他默认,点打开,然后按照提示输入账号密码登录,这里如果告诉连不上,可以稍微等一会儿再试试。
  • OS X 上可在终端连接 SSH,用 ssh pi@192.168.10.106 命令,当然换成你的树莓派的 IP,如果不出意外,会提示你输密码,然后就会登录成功,再次强调不出意外,OS X 上这一步似乎经常容易出意外,有时候树莓派重启后如果分配到了不同于上次的 IP,这边就会登录不上,提示没有访问权限,好像是 SSH Key 的问题,但是一直没搞清楚。

登录成功后,树莓派就到嘴边了。

3. 配置

不过要先稍微配置一下,先 sudo raspi-config 命令进入一个比较简陋的配置界面,选择「Expand Filesystem」,这样会把整个系统的可用空间扩充到储存卡的大小,据说不扩充的话,多余的空间用不了,不过我没尝试。扩充完成后会提示你重启,回车重启。

然后电脑上的 SSH 连接就断了(这句废话)。

过个几十秒,再一次 SSH。

执行 ping baidu.com 试试看能不能连上网(一定要 ping 百度哈哈,这已经是百度存在的大部分意义了)。

我个人感觉是最好设置个静态 IP,就像之前说的 OS X 上面好像如果树莓派换 IP 的就会出问题,所以还是设置个静态 IP 靠谱。sudo vi /etc/network/interfaces 进入网络接口配置文件(我看了好多教程,里面大部分都是用 nano 来编辑文件,不知为何,我感觉还是 vi 用着顺手,我是装了个 vimsudo apt-get install vim 可安装 vim),把 iface eth0 inet dhcp 中的 dhcp 改成 static,并在这行下面加上四行:

address 192.168.10.123
netmask 255.255.255.0
gateway 192.168.10.1
dns-nameservers 192.168.10.1

IP 地址和网关要换成你自己的,网关填路由器地址,DNS 域名服务器也填路由器地址好了,改好后保存退出。

4. 远程桌面

sudo apt-get install tightvncserver 命令安装 vncserver,安装成功后 vncpasswd 命令设置一个密码,然后执行 vncserver :1 -geometry 800x600 即可启动 1 号桌面,同理 :2 就是 2 号桌面,后面的 -geometry 800x600 就是设置分辨率。这个地方其实直接 vncserver 命令就好了,不加参数就是默认启动 1 号桌面,分辨率 1024x768。

电脑上下载 VNC Viewer(官网下载),下好打开输入树莓派 IP 地址及桌面号,形如 192.168.10.123:1,点「Connect」,之后输入密码即可登录,vncserver -kill :1 命令可关闭 1 号桌面,不常用。

要让 1 号桌面开机启动,先 sudo vi /etc/init.d/tightvncserver 创建一个新文件,把下面内容复制进去:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          tightvncserver
# Required-Start:    $local_fs
# Required-Stop:     $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start/stop tightvncserver
### END INIT INFO
# More details see:
# http://www.penguintutor.com/linux/tightvnc
### Customize this entry
# Set the USER variable to the name of the user to start tightvncserver under
export USER=’pi’
### End customization required
eval cd ~$USER
case ”$1” in
  start)
    # 启动命令行。此处自定义分辨率、控制台号码或其它参数。
    su $USER -c ’/usr/bin/tightvncserver -geometry 800x600 :1’
    echo ”Starting TightVNC server for $USER ”
    ;;
  stop)
    # 终止命令行。此处控制台号码与启动一致。
    su $USER -c ’/usr/bin/tightvncserver -kill :1’
    echo ”Tightvncserver stopped”
    ;;
  *)
    echo ”Usage: /etc/init.d/tightvncserver {start|stop}”
    exit 1
    ;;
esac
exit 0

保存退出。

再执行下面两行命令:

sudo chmod 755 /etc/init.d/tightvncserver
sudo update-rc.d tightvncserver defaults

不过……话说,上面的方法我试了,但每次开机并没有自动启动桌面,不过很简单啦,在 /etc/rc.local 文件里面加上一行 vncserver :1 -geometry 800x600 就是开机启动了。

5. 更改键盘布局及语言和地区

因为树莓派是英国产的嘛,所以默认键盘布局是英国的,于是有些符号你会发现打不出来,所以我们要换成美式键盘。

sudo raspi-config 进入配置界面,选「International」,进去后再选「Change Keyboard」,进入后,找到「Generic 104-key PC」(说到这里的104-key,我看了两个教程,一个说 101,一个说 105,于是我数了一下我的键盘,是 104 个键,于是就选了 104),回车确定进入「Keyboard layout」,放眼望去全是「UK」,所以选「Other」,进去后找到「English(US)」(可能要翻不少页才能找到),回车确定,然后再选「English(US, alternative international)」,然后一路 OK,退出后,重启系统(如果你不知道重启的命令的话……Google 去吧)即可。这里说是要重启系统,不过我没重启的时候就已经可以正常用了。

然后还要改一个地区,同样是 sudo raspi-config 进入配置界面,选「International」,然后「Change Locale」,去掉「en_GB.UTF-8 UTF-8」,勾上「en_US.UTF-8 UTF-8」、「zh_CN.UTF-8 UTF-8」和「zh_CN.GBK GBK」,空格键是打勾或取消勾,Tab 键跳到 Ok 按钮,回车进入下一页,然后就是选系统默认语言了,我选了「en_US.UTF-8」,貌似如果选「zh_CN.UTF-8」那么桌面就是中文界面(虽然这样真的很奇怪)。至于中文输入法,反正我还没装,据说一个叫 Scim 的输入法不错,sudo apt-get -y install scim-pinyin 可安装,要加装五笔则再执行 sudo apt-get -y install scim-tables-zh

6. 总结

先写这么多,都是把别人的教程里面可用的部分拿过来的,本文的参考资料在下面,后面有空的话,再写一篇关于那几个配件的。

7. 参考资料

树莓派上手三天总结

Octopress 博客 deploy 失败的解决办法

创建于
分类:Misc
标签:OctopressBlog

参考资料:rake gen_deploy rejected in Octopress

今天在新系统里面设置 octopress 的时候遇到一个问题,就是 rake deploy 总是失败,有形如下面的错误提示:

 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:richardchien/richardchien.github.io.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

经过各种搜索,似乎是远程仓库和本地的代码不一致,需要强行把本地的内容 push 到远程仓库,解决办法如下:

octopress 文件夹找到 Rakefile 文件,打开,找到 system "git push origin #{deploy_branch}" 这一行,改成 system "git push origin +#{deploy_branch}",然后再执行 rake deploy 就可以成功了,之后远程仓库的代码就和本地一致了,然后把 Rakefile 文件改回原来的 system "git push origin #{deploy_branch}",就 OK 了。

Octopress 博客 deploy 失败的解决办法

更换电脑或多台电脑同时使用 Octopress

创建于
分类:Misc
标签:OctopressBlog

如果你的电脑上的 Octopress 文件夹被误删了,或者你换电脑了,或者你想在多台电脑上同时都可以写博客,那么本教程将教你如何解决。

下面内容翻译自 Clone Your Octopress to Blog From Two Places

重新创建本地 Octopress 仓库

下面的内容将教你如何为已有的 Octopress 博客重新创建本地目录结构。

将你的博客克隆到新设备

首先你需要将仓库的 source 分支克隆到本地的 octopress 文件夹。

git clone -b source git@github.com:username/username.github.com.git octopress

然后将 master 分支克隆到 _deploy 子文件夹。

cd octopress
git clone git@github.com:username/username.github.com.git _deploy

接着运行 rake 安装命令来完成配置。

gem install bundler
rbenv rehash # If you use rbenv, rehash to be able to run the bundle command
bundle install
rake setup_github_pages

它会提示你输入仓库的 URL。

Enter the read/write url for your repository
(For example, 'git@github.com:your_username/your_username.github.com')

到这里你就成功地将 Octopress 博客拷贝到了本地。

从两个不同设备推送变更

如果你想在多台电脑上写博客,你需要确保在切换电脑之前把所有变更的内容都推送到 GitHub。即,在切换前,一旦变更了内容,就要执行下面命令:

rake generate
git add .
git commit -am "some comment here."
git push origin source #更新远程仓库的 source 分支
rake deploy            #更新远程仓库的 master 分支

接着,切换到另一台电脑后,你需要拉取之前推送的改变。

cd octopress
git pull origin source #更新本地仓库的 source 分支
cd ./_deploy
git pull origin master #更新本地仓库的 master 分支

当然如果把 Octopress 安装到U盘的话部署会更简单。

更换电脑或多台电脑同时使用 Octopress