Jshell 启动错误 build 9-ea+121


Exception in thread "main" java.lang.InternalError: Launching execution engine threw: Failed remote launch: com.sun.jdi.CommandLineLaunch (defaults: home=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home, options=, main=, suspend=true, quote=", vmexec=java) -- {home=home=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home, options=options=, main=main=jdk.internal.jshell.remote.RemoteAgent 57696, suspend=suspend=true, quote=quote=", vmexec=vmexec=java}
at jdk.jshell.JShell.executionControl(jdk.jshell@9-ea/JShell.java:714)
at jdk.jshell.Unit.classesToLoad(jdk.jshell@9-ea/Unit.java:275)
at jdk.jshell.Eval.lambda$compileAndLoad$15(jdk.jshell@9-ea/Eval.java:580)
at java.util.stream.ReferencePipeline$7$1.accept(java.base@9-ea/ReferencePipeline.java:269)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(java.base@9-ea/ArrayList.java:1477)
at java.util.stream.AbstractPipeline.copyInto(java.base@9-ea/AbstractPipeline.java:484)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(java.base@9-ea/AbstractPipeline.java:474)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(java.base@9-ea/ReduceOps.java:913)
at java.util.stream.AbstractPipeline.evaluate(java.base@9-ea/AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(java.base@9-ea/ReferencePipeline.java:511)
at jdk.jshell.Eval.compileAndLoad(jdk.jshell@9-ea/Eval.java:581)
at jdk.jshell.Eval.declare(jdk.jshell@9-ea/Eval.java:441)
1 127.0.0.1 Hais-MacBook-Pro
at jdk.jshell.Eval.processMethod(jdk.jshell@9-ea/Eval.java:372)
at jdk.jshell.Eval.eval(jdk.jshell@9-ea/Eval.java:127)
at jdk.jshell.JShell.eval(jdk.jshell@9-ea/JShell.java:393)
at jdk.internal.jshell.tool.JShellTool.processCompleteSource(jdk.jshell@9-ea/JShellTool.java:2114)
at jdk.internal.jshell.tool.JShellTool.processSource(jdk.jshell@9-ea/JShellTool.java:2102)
at jdk.internal.jshell.tool.JShellTool.processSourceCatchingReset(jdk.jshell@9-ea/JShellTool.java:789)
at jdk.internal.jshell.tool.JShellTool.run(jdk.jshell@9-ea/JShellTool.java:769)
at jdk.internal.jshell.tool.JShellTool.startUpRun(jdk.jshell@9-ea/JShellTool.java:706)
at jdk.internal.jshell.tool.JShellTool.resetState(jdk.jshell@9-ea/JShellTool.java:663)
at jdk.internal.jshell.tool.JShellTool.start(jdk.jshell@9-ea/JShellTool.java:483)
at jdk.internal.jshell.tool.JShellTool.start(jdk.jshell@9-ea/JShellTool.java:462)
at jdk.internal.jshell.tool.JShellTool.main(jdk.jshell@9-ea/JShellTool.java:452)
Caused by: java.lang.InternalError: Failed remote launch: com.sun.jdi.CommandLineLaunch (defaults: home=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home, options=, main=, suspend=true, quote=", vmexec=java) -- {home=home=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home, options=options=, main=main=jdk.internal.jshell.remote.RemoteAgent 57696, suspend=suspend=true, quote=quote=", vmexec=vmexec=java}
at jdk.internal.jshell.jdi.JDIConnection.reportLaunchFail(jdk.jshell@9-ea/JDIConnection.java:353)
at jdk.internal.jshell.jdi.JDIConnection.launchTarget(jdk.jshell@9-ea/JDIConnection.java:319)
at jdk.internal.jshell.jdi.JDIConnection.open(jdk.jshell@9-ea/JDIConnection.java:120)
at jdk.internal.jshell.jdi.JDIEnv.init(jdk.jshell@9-ea/JDIEnv.java:49)
at jdk.internal.jshell.jdi.JDIExecutionControl.jdiGo(jdk.jshell@9-ea/JDIExecutionControl.java:425)
at jdk.internal.jshell.jdi.JDIExecutionControl.start(jdk.jshell@9-ea/JDIExecutionControl.java:95)
at jdk.jshell.JShell.executionControl(jdk.jshell@9-ea/JShell.java:712)
... 23 more
Caused by: com.sun.jdi.connect.VMStartException: VM initialization failed for: /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,address=Hais-MacBook-Pro:57697,suspend=y jdk.internal.jshell.remote.RemoteAgent 57696
at com.sun.tools.jdi.AbstractLauncher$Helper.launchAndAccept(jdk.jdi@9-ea/AbstractLauncher.java:193)
at com.sun.tools.jdi.AbstractLauncher.launch(jdk.jdi@9-ea/AbstractLauncher.java:132)
at com.sun.tools.jdi.SunCommandLineLauncher.launch(jdk.jdi@9-ea/SunCommandLineLauncher.java:225)
at jdk.internal.jshell.jdi.JDIConnection.launchTarget(jdk.jshell@9-ea/JDIConnection.java:312)
... 28 more

https://bugs.openjdk.java.net/browse/JDK-8131029
这个问题在新版的已经被修复了,但是如果碰到了,可以修改 /etc/hosts 来绕过(增加一个本地计算机名字的 loop ip)

本地计算机名字可以用
uname -n
来查看

参考这个文章找到的 workaround

独立编译 Skia for Android

最近想了解下 Skia 相关的东西,想利用其中的一些 API 来做做优化,所以打算独立编译一个版本试试看。

https://skia.org/user/quick/android

使用的代码版本


commit 81bdbf8bed8b739c2b65ac576e89d0258276e6dc
Author: caryclark
Date: Wed Oct 21 04:16:19 2015 -0700

编译环境

Ubuntu 14.04.2

直接按照官方说明就可以编译出来,我这里是不想去下载一遍 NDK,所以进行了点改动。


http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin

如果机器上已经安装过对应版本的 NDK,可以修改以下文件直接生成 TOOLCHAIN(这个步骤不是必须的)

/mnt/extra/skia/platform_tools/android/bin/utils/setup_toolchain.sh


function default_toolchain() {
- TOOLCHAINS=${SCRIPT_DIR}/../toolchains
+ TOOLCHAINS=/home/ubuntu/dev

ANDROID_ARCH=${ANDROID_ARCH-arm}
LLVM=3.6
@@ -50,19 +50,13 @@ function default_toolchain() {
exportVar ANDROID_TOOLCHAIN "${TOOLCHAINS}/${TOOLCHAIN}/bin"

if [ ! -d "$ANDROID_TOOLCHAIN" ]; then
- mkdir -p $TOOLCHAINS
pushd $TOOLCHAINS
- curl -o $NDK.bin https://dl.google.com/android/ndk/android-ndk-$NDK-$HOST-x86_64.bin
- chmod +x $NDK.bin
- ./$NDK.bin -y
./android-ndk-$NDK/build/tools/make-standalone-toolchain.sh \
--arch=$ANDROID_ARCH \
--llvm-version=$LLVM \
--platform=android-$API \
--install_dir=$TOOLCHAIN
cp android-ndk-$NDK/prebuilt/android-$ANDROID_ARCH/gdbserver/gdbserver $TOOLCHAIN
- rm $NDK.bin
- rm -rf android-ndk-$NDK
popd
fi

生成过一次 TOOLCHAIN 之后也可以把

export ANDROID_TOOLCHAIN=/home/ubuntu/dev/arm-r10e-14/bin
export PATH=$ANDROID_TOOLCHAIN:$PATH

手动加在到配置文件里面去(这个步骤不是必须的)


./platform_tools/android/bin/android_ninja -d nexus_5

然后就是等待编译,如果中途编译 APK 的时候却少一些特定版本的 Build Tool 的时候修改下 App 当中使用版本就好了,或者也可以去更新代码当中对应的版本
App 代码位于

/mnt/extra/skia/platform_tools/android/apps/

编译完成之后就可以在
/mnt/extra/skia/out/config/android-nexus_5/Debug
下看到 so 了

Tips about Android Studio

还是开一篇记录Android Studio有关的各种诡异问题。
首先感谢一下,一直以Preview状态出现的AS最近终于是Beta了,普天同庆!
AS有好也有不好,但是感觉就是用了它就回不去Eclipse了。

之前基本在版本升级的时候都会或多或少出现点问题,有的坑了很久,记录分享下,避免在这上面浪费时间!

1. 一直提示refreshing/configure project,很长时间都不会结束。
经过观察这个是在现在一些依赖包,有800多M,网速慢的话估计很难等到下载完成的时候。有个解决办法就是在Preferences当中选择Gradle为Offline work模式。
当然也可以全局配置Offline work,打开$HOME/.gradle/init.gradle,如果没有该文件就创建一个,然后在里面添加

allprojects {
    repositories {
        .......
    }

    gradle.startParameter.setOffline(true)
}

主要就是这句gradle.startParameter.setOffline(true)啦,不是很明白的可以去网络上搜索了解下,
http://www.gradle.org/docs/current/userguide/init_scripts.html
http://www.gradle.org/docs/current/javadoc/org/gradle/StartParameter.html
如果发现创建项目之后,已经处于这种卡住的状态,可以断开网络链接,一会儿这种卡住状态就会消失了。

2. 2014-07-01 10:24:55,919 [1292862] INFO – .project.GradleProjectImporter – The project is using an unsupported version of the Android Gradle plug-in (0.11.2)
鼠标移动到build.gradle当中的classpath值上面,根据提示修改就可以

    dependencies {
        classpath 'com.android.tools.build:gradle:0.11.+'
    }

然后执行gradlew clean assembleDefaultFlavor或者直接clean build工程,应该就可以解决。

3. 还有个东西叫做代理,关键的时候可以用上。

Building Android apps with Jenkins CI

在前东家,工程相关的都有专门的组来做,比如自动化的代码审查,编译,发布,测试,缺陷追踪等等系统。。。甚至当时就有人说就XXX的很多系统,即使司龄很大的员工都不一定都能使用的很熟练,因为实在是太多了。。。相反的是现在都没有,万事都需要自己动手,不过也还好,可以挑选些真的是非常有帮助的自动化系统来帮助我们提高工作效率(比如之前我一直都是不大记住写周报,其实我觉得那应该是小时报,不过还好,我当时的老大和部门经理对这个要求没有那么严格,我也不是偷懒的员工,所以基本是马马虎虎过了,但是我还是比较喜欢例如某些任务管理系统,用任务管理系统主要是防止自己忘记,一些大任务可以拆分成小任务,并且标记完成状况,或者是你在任务上撰写出你自己的意见/建议和老大进行沟通),今天就以自动化编译系统为起点来逐步搭建/定制出一个比较理想的持续集成系统。。。当然这是一个长期的任务,不是说它非常难,只是自己时间也不是特别多,会先挑些目前比较重要的东西来加入进来(building,lint,findbugs,monkey)。。。
今天先说编译系统,而且先是看基于Ant的,假设源码是放在GitHub上,比如https://github.com/guohai/and-expandable-listview/这个项目,我希望我的系统在每隔一定时间或者每次代码提交之后自己启动一次编译,第一这可以排错,防止开发人员不小心漏提交代码,或者merge代码的时候出错,尽早的发现这类比较低级的错误,编译系统编译失败会立刻邮件通知你,而不是你在本地编译测试了很多遍,信心满满的把代码提交(当然有人说会有人工代码审查,但是人工也不一定能发现所有错误,特别是编译错误),回家睡觉了,另外一个时区的同事刚开始新的一天,检出你的代码,发现根本编译不通过,那么他是该打你的电话叫醒你修改代码呢?还是等你第二天来修改呢?明显他两样都不想,所以最好是你保证自己提交的代码都是至少可以编译通过的(会有人说这么简单的事情会做不到吗?事实是当你很多人协同工作的时候,这个事情确实是有可能发生,很多次在XXX公司经历过一两天编译系统无法出一个可以通过的版本,当然这不是在黑前东家,只是事情确实有,而且公司也在逐步改用好一点的编译方式减少这种一个编译错误导致整个系统都无法编译出ROM的窘境);第二这个可以利用一些工具直接将编译的成果推送到自动化测试的服务里面完成基础的自动化测试,这样岂不是很好,减少些不必要的人工操作;第三有权限的人可以随时下载一个他需要的版本程序来安装运行,而不需要让你给他邮件发一个版本或者他自己安装编译环境,检出代码来完成编译。可能还有很多理由来需要这样一个系统,不过这里已经够了。。。
Jenkins的安装,基本使用就不说了,具体查看https://wiki.jenkins-ci.org/display/JENKINS/Use+Jenkins上面,我这里主要安装了一个GitHub插件,在Manage Jenkins->Manage Plugins当中可以很方便的完成,因为我本地都有Android SDK, Ant, Java等等一些基础环境,而且这些都已经在PATH当中,所以集成起来就很简单。然后就是创建任务, Jenkins当中称为job,然后就是配置该job,比如GitHub项目位置,仓库地址,keystore for release build,编译预处理,归档编译产物,以及触发编译条件,如下图:
Screen Shot 2014-01-27 at 12.00.47 AM
Screen Shot 2014-01-27 at 12.01.53 AM
Screen Shot 2014-01-27 at 12.14.36 AM
Screen Shot 2014-01-27 at 12.15.05 AM
Screen Shot 2014-01-27 at 12.34.11 AM
Screen Shot 2014-01-27 at 12.43.14 AM
这样就基本可以搭建出一个自动化的编译系统了,后面有机会来研究下基于Gradle的,毕竟这才是Google目前主导的方向。
当然你可以根据自己的需要调节更改配置来达到想要自己的目的,比如GitHub有提交就触发编译,只归档特定名字的文件等等,是否删除原有归档,如果新的版本有成功编译出来的话,等等可能还有更多的技巧。

洗完个澡,顺便试了下Gradle的,你可以在Jenkins当中下载Gradle插件,也可以不用专门下载,自己写个简单的脚本完成,比如我这里就是:
Screen Shot 2014-02-06 at 12.25.49 AM
而且也只有上述这一段不一样,其他都跟Ant的基本类似,不过生成需要归档的apk的路径可能需要改改,这里有个硬编码的ANDROID_HOME,目前没有发现比较好的解决方法,如果您知道如何解决,不吝赐教。在Ant版本中,我们sdk.dir是通过android update project生成的,但是在Gradle的项目当中似乎无法生成,没有SDK路径就会编译出错,比如:

* Where:
Build file '/Users/guohai/.jenkins/jobs/Gradle-Proj-Test/workspace/xxx/build.gradle' line: 9

* What went wrong:
A problem occurred evaluating project ':beckon'.
> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.
于是就先硬编码这个ANDROID_HOME变量先跳过这一问题,有空再来研究看看。
1
export ANDROID_HOME=/Users/guohai/Dev/android-sdk-macosx
chmod +x ./gradlew 
./gradlew clean assembleDefaultFlavorDebug

当然这gradlew后面跟的task可以自己调整,比如debug版本或者release版本,或者编译子项目(./gradlew :sub-project:clean,sub-project就是实际子项目的名字)。

P.S.
通知邮件发送使用Jenkins Email Extension Plugin
不过在配置的时候出现
501 mail from address must be same as authorization user
参考http://bbs.csdn.net/topics/390353671设置System Admin e-mail address就可以了。

Updated at 2015.12.25
升级到最新版本之后出现即使代码没有改变,也不停的出 Started by SCM
https://issues.jenkins-ci.org/browse/JENKINS-17614
这是一个 bug,我这里导致的原因,是有多个 branch 的名字包含 develop,然后进行 branch 指定的时候,指定 develop 就分不清了。
删掉或者改名 branch 就可以正常工作了。

另外在 headless 的服务器上下载 Android SDK ,如果只想下载/更新指定的包,那么请参照这里。
http://tools.android.com/recent/updatingsdkfromcommand-line
简单来说先通过

$ android list sdk

查看有哪些更新
然后指定需要安装的包

android update sdk --no-ui --filter tools,3,8

不清楚的可以

android update sdk --help

看一下

比如强制使用 http 协议列出所有更新(包括已废弃的)

android list sdk --no-https --all

注意,调用更新的时候也要带 –all 参数,否则编号会错乱

Python连接MySQL数据库

UPDATE: 其实目前没有用到多少Python知识来写,基本都是直接调用的已有的命令来完成备份和邮件通知功能 @@
为了写个简单的备份程序,我决定使用Python来实现,几年前有看过点Python,今天就来试试看。通常我们连接数据库会用到对应的连接API,比如Java的JDBC(Java_Database_Connectivity‎),顶多中间增加个连接池之类的,因为我们这里很简单,所以只需要一个类似的API,我们这里用MySQL-python来完成。先安装这个东西,我采用1.2.3版本,当然我这里宿主机器是CentOS。

[guohai@Knight-on-the-Cloud installation]$ wget http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.3/MySQL-python-1.2.3.tar.gz/download
[guohai@Knight-on-the-Cloud installation]$ tar xvf MySQL-python-1.2.3.tar.gz
[guohai@Knight-on-the-Cloud installation]$ cd MySQL-python-1.2.3

vim site.cfg

# The path to mysql_config.
# Only use this if mysql_config is not on your PATH, or you have some weird
# setup that requires it.
mysql_config = /usr/bin/mysql_config

正如这上面所说,之前我安装MySQL有改动默认位置,所以这里要单独指定下。

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build

if errors(“no setuptools”) showing

cd ..
wget https://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c10-py2.6.egg --no-check-certificate
sh setuptools-0.6c10-py2.6.egg

然后

cd MySQL-python-1.2.3

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build

if errors(“Python.h: No such file or directory”) showing

[root@Knight-on-the-Cloud installation]# yum -y install python-devel

继续

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build
running build
running build_py
copying MySQLdb/release.py -> build/lib.linux-x86_64-2.6/MySQLdb
running build_ext
building '_mysql' extension
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -Dversion_info=(1,2,3,'final',0) -D__version__=1.2.3 -I/usr/include/mysql -I/usr/include/python2.6 -c _mysql.c -o build/temp.linux-x86_64-2.6/_mysql.o -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fwrapv -fPIC -DUNIV_LINUX -DUNIV_LINUX
_mysql.c:36:23: error: my_config.h: No such file or directory
_mysql.c:38:19: error: mysql.h: No such file or directory
_mysql.c:39:26: error: mysqld_error.h: No such file or directory
_mysql.c:40:20: error: errmsg.h: No such file or directory
[root@Knight-on-the-Cloud include]# yum -y install mysql-devel

[root@Knight-on-the-Cloud mysql]# pwd
/usr/include/mysql
[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build
running build
running build_py
copying MySQLdb/release.py -> build/lib.linux-x86_64-2.6/MySQLdb
running build_ext
building '_mysql' extension
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -Dversion_info=(1,2,3,'final',0) -D__version__=1.2.3 -I/usr/include/mysql -I/usr/include/python2.6 -c _mysql.c -o build/temp.linux-x86_64-2.6/_mysql.o -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fwrapv -fPIC -DUNIV_LINUX -DUNIV_LINUX
In file included from /usr/include/mysql/my_config.h:14,
                 from _mysql.c:36:
/usr/include/mysql/my_config_x86_64.h:1082:1: warning: "HAVE_WCSCOLL" redefined
In file included from /usr/include/python2.6/pyconfig.h:6,
                 from /usr/include/python2.6/Python.h:8,
                 from pymemcompat.h:10,
                 from _mysql.c:29:
/usr/include/python2.6/pyconfig-64.h:808:1: warning: this is the location of the previous definition
gcc -pthread -shared build/temp.linux-x86_64-2.6/_mysql.o -L/usr/lib64/mysql -L/usr/lib64 -lmysqlclient_r -lz -lpthread -lcrypt -lnsl -lm -lpthread -lssl -lcrypto -lpython2.6 -o build/lib.linux-x86_64-2.6/_mysql.so

编译好了就安装

[root@Knight-on-the-Cloud MySQL-python-1.2.3]# python setup.py install
running install
running bdist_egg
running egg_info
writing MySQL_python.egg-info/PKG-INFO
writing top-level names to MySQL_python.egg-info/top_level.txt
writing dependency_links to MySQL_python.egg-info/dependency_links.txt
reading manifest file 'MySQL_python.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching 'MANIFEST'
warning: no files found matching 'ChangeLog'
warning: no files found matching 'GPL'
writing manifest file 'MySQL_python.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
copying MySQLdb/release.py -> build/lib.linux-x86_64-2.6/MySQLdb
running build_ext
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/egg
copying build/lib.linux-x86_64-2.6/_mysql.so -> build/bdist.linux-x86_64/egg
copying build/lib.linux-x86_64-2.6/_mysql_exceptions.py -> build/bdist.linux-x86_64/egg
creating build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/__init__.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/cursors.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/connections.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/times.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/release.py -> build/bdist.linux-x86_64/egg/MySQLdb
creating build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/REFRESH.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/CLIENT.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/FIELD_TYPE.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/FLAG.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/__init__.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/CR.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/ER.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/converters.py -> build/bdist.linux-x86_64/egg/MySQLdb
byte-compiling build/bdist.linux-x86_64/egg/_mysql_exceptions.py to _mysql_exceptions.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/cursors.py to cursors.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/connections.py to connections.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/times.py to times.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/release.py to release.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/REFRESH.py to REFRESH.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/CLIENT.py to CLIENT.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/FIELD_TYPE.py to FIELD_TYPE.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/FLAG.py to FLAG.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/CR.py to CR.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/ER.py to ER.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/converters.py to converters.pyc
creating stub loader for _mysql.so
byte-compiling build/bdist.linux-x86_64/egg/_mysql.py to _mysql.pyc
creating build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
writing build/bdist.linux-x86_64/egg/EGG-INFO/native_libs.txt
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/MySQL_python-1.2.3-py2.6-linux-x86_64.egg' and adding 'build/bdist.linux-x86_64/egg' to it
removing 'build/bdist.linux-x86_64/egg' (and everything under it)
Processing MySQL_python-1.2.3-py2.6-linux-x86_64.egg
Copying MySQL_python-1.2.3-py2.6-linux-x86_64.egg to /usr/lib64/python2.6/site-packages
Adding MySQL-python 1.2.3 to easy-install.pth file

Installed /usr/lib64/python2.6/site-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg
Processing dependencies for MySQL-python==1.2.3
Finished processing dependencies for MySQL-python==1.2.3

接着再来试下

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python
Python 2.6.6 (r266:84292, Jul 10 2013, 22:48:45) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb
/usr/lib64/python2.6/site-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/_mysql.py:3: UserWarning: Module _mysql was already imported from /usr/lib64/python2.6/site-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/_mysql.pyc, but /home/guohai/apps/installation/MySQL-python-1.2.3 is being added to sys.path

上面这些UserWarning是说MySQL模块已经引入了,但是你在的目录又被加入到sys.path,也就是重复引入。这个时候你只要跳出我们刚刚编译模块的目录就好了。
一切都正常之后我们来写个简单的程序试试看。

[guohai@Knight-on-the-Cloud npc]$ python
Python 2.6.6 (r266:84292, Jul 10 2013, 22:48:45) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb

>>> help(MySQLdb) // 不懂的话就可以这样查询

>>> conn = MySQLdb.connect('localhost', 'wp_user', 'wp_passwd', 'wp_db')
>>> cur = conn.cursor()
>>> cur.execute('select user_login, user_registered from wp_users')
1L
>>> rst = cur.fetchall()
>>> print rst
(('admin', datetime.datetime(2011, 3, 19, 0, 8, 9)),)
>>> cur.close()
>>> conn.close()

与我们通过

[guohai@Knight-on-the-Cloud npc]# mysql -h localhost -u wp_user -p

查询出来的一样

mysql> select * from wp_users \G
*************************** 1. row ***************************
                 ID: 1
         user_login: admin
          user_pass: #CLASSIFIED#
      user_nicename: #CLASSIFIED#
         user_email: #CLASSIFIED#
           user_url: #CLASSIFIED#
    user_registered: 2011-03-19 00:08:09
user_activation_key: #CLASSIFIED#
        user_status: #CLASSIFIED#
       display_name: #CLASSIFIED#
1 row in set (0.00 sec)

也就是说我们简单的连接数据库成功了,到此基础环境算是搭建好了。

另外简单写了个备份程序(https://github.com/guohai/my-conf/blob/master/wp_backup_npc.py),待完善。。。

然后编写一个crontab就可以让它定时执行(不会crontab的请参见http://linux.vbird.org/linux_basic/redhat6.1/linux_11cron.php)

9 16 * * 0 cd /home/guohai/apps/npc/ && python wp_backup_npc.py &> /dev/null

就是在每周日的16点9分会运行一次后面的命令(跳转到npc文件夹,执行这个python命令,如果有一些信息输出的话就把信息重定向到/dev/null,如果有信息输出而不重定向这些信息的话,crontab就会认为这个命令是执行失败的,所以就会给你的邮箱发封提醒邮件,时间久了你的/var/spool/mail/账户下就会有很多邮件,所以这里我把一切输出信息都重定向到/dev/null了)。

当然你也可以阅读简单的介绍了解crontab脚本如何编写,如下:

# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use ‘*’ in these fields (for ‘any’).#
# Notice that tasks will be started based on the cron’s system
# daemon’s notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command

迁移博客到Digital Ocean

搜了下据说性价比还可以的VPS,似乎我在上海ping值很高,用用看吧(可以按小时付费)。

注册账号,创建VPS,拿到root用户

ssh root@162.243.117.95

创建常用账户

ssh guohai@162.243.117.95

拿到之后当然先装些基础的软件,比如FTP服务,SSL等等,这个看自己的爱好。
摸出我当年做各种MIS的经验,什么Nginx/MySQL/PHP,不过PHP我之前没有弄过,照着网络上简单的搞搞。

[root@Knight-on-the-Cloud ~]# yum -y install vsftpd
参照
http://my.oschina.net/u/130017/blog/15229
基础配置

local_enable=YES
ascii_upload_enable=YES
ascii_download_enable=YES
write_enable=YES

然后就是MySQL
[root@Knight-on-the-Cloud log]# yum -y install mysql-server

[root@Knight-on-the-Cloud log]# service mysqld start
Initializing MySQL database: WARNING: The host ‘Knight-on-the-Cloud’ could not be looked up with resolveip.
This probably means that your libc libraries are not 100 % compatible
with this binary MySQL version. The MySQL daemon, mysqld, should work
normally with the exception that host name resolving will not work.
This means that you should use IP addresses instead of hostnames
when specifying MySQL privileges !
Installing MySQL system tables…
OK
Filling help tables…
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/usr/bin/mysqladmin -u root password ‘new-password’
/usr/bin/mysqladmin -u root -h Knight-on-the-Cloud password ‘new-password’

Alternatively you can run:
/usr/bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default. This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /usr ; /usr/bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /usr/mysql-test ; perl mysql-test-run.pl

Please report any problems with the /usr/bin/mysqlbug script!

[ OK ]
Starting mysqld: [ OK ]

这样装好之后就记住你MySQL的root密码啦,记得所有都是最小权限原则,不用的就不需要开。

移动MySQL数据文件位置,当然先得停止MySQL服务。
这几个命令都是在OS的root权限下进行的。
mkdir -p /usr/local/mysql
cp -afir /var/lib/mysql/* /usr/local/mysql

vim /etc/my.cnf

[mysqld]
datadir=/usr/local/mysql
socket=/usr/local/mysql/mysql.sock
default-character-set=utf8
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

[mysql]
default-character-set=utf8

vim /etc/init.d/mysqld
datadir=/usr/local/mysql

启动MySQL,并登录试试。
mysql -h 127.0.0.1 -P 3306 -u root -p
或者用
mysql -u root -p
来登录

最后还是创建一个软链接,主要是后面PHP程序连过来的时候是找的默认这个地方(/var/lib/mysql/mysql.sock)
[root@Knight-on-the-Cloud mysql]# mkdir -p /var/lib/mysql/
[root@Knight-on-the-Cloud mysql]# ln -s /usr/local/mysql/mysql.sock /var/lib/mysql/mysql.sock

如果数据库能启动,看看/var/log/mysqld.log里面日志都正常的话,这步就应该好了。

安装Nginx
https://www.digitalocean.com/community/articles/how-to-compile-nginx-from-source-on-an-centos-6-4-x64-vps
编译参数如下

./configure \
--user=nginx                          \
--group=nginx                         \
--prefix=/etc/nginx                   \
--sbin-path=/usr/sbin/nginx           \
--conf-path=/etc/nginx/nginx.conf     \
--pid-path=/var/run/nginx.pid         \
--lock-path=/var/run/nginx.lock       \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module        \
--with-http_stub_status_module        \
--with-http_ssl_module                \
--with-pcre                           \
--with-file-aio                       \
--with-http_realip_module             \
--without-http_scgi_module            \
--without-http_uwsgi_module           \
--without-http_fastcgi_module

注意,如果你弄好这些启动的话,发现
nginx: [emerg] unknown directive “fastcgi_pass”
nginx不要带这个参数–without-http_fastcgi_module编译,这样就表示Nginx集成了fastCGI的功能,也就不需要使用spawn-fcgi等等。

配置/etc/nginx/nginx.conf
types_hash_bucket_size 64;
server_names_hash_bucket_size 128;

加入
include /etc/nginx/conf.d/*.conf;
到/etc/nginx/nginx.conf

安装PHP
https://www.digitalocean.com/community/articles/how-to-install-linux-nginx-mysql-php-lemp-stack-on-centos-6

如果没有启用Nginx或者PHP的fastCGI,就只能安装
http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz
了,因为我们在编译Nginx的时候有http_fastcgi_module,所以就不需要这个了

Nginx出现403,
原因http://www.linuxidc.com/Linux/2013-06/85625.htm

vim /etc/nginx/nginx.conf
user guohai;

root 18137 0.0 0.2 44552 1092 ? Ss 13:30 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
guohai 18139 0.0 0.3 44956 1708 ? S 13:30 0:00 nginx: worker process
guohai 18140 0.0 0.3 44956 1708 ? S 13:30 0:00 nginx: worker process
guohai 18141 0.0 0.3 44956 1680 ? S 13:30 0:00 nginx: worker process
guohai 18142 0.0 0.3 44956 1692 ? S 13:30 0:00 nginx: worker process

访问目录权限
[guohai@Knight-on-the-Cloud html]$ ls -l
total 4
drwxrwxr– 2 guohai guohai 4096 Nov 27 11:07 guoh.org

[guohai@Knight-on-the-Cloud guoh.org]$ ls -l
total 8
-rw-rw-r– 1 guohai guohai 0 Nov 27 13:37 hello.txt
-rw-rw-r– 1 guohai guohai 612 Nov 27 11:33 index.html
-rw-rw-r– 1 guohai guohai 617 Nov 27 11:05 info.php

PHP页面出现,
404, No input file specified.
vim /etc/php-fpm.d/www.conf
Unix user/group of processes里面用户和组控制的和所需要访问文件属于同一个用户和组就好,我这里都改成guohai。

root 18409 0.0 0.7 125452 3952 ? Ss 15:09 0:00 php-fpm: master process (/etc/php-fpm.conf)
guohai 18410 0.0 1.0 126088 5208 ? S 15:09 0:00 php-fpm: pool www
guohai 18411 0.0 1.0 125972 5216 ? S 15:09 0:00 php-fpm: pool www
guohai 18412 0.0 0.8 125972 4136 ? S 15:09 0:00 php-fpm: pool www
guohai 18413 0.0 0.8 125452 4124 ? S 15:09 0:00 php-fpm: pool www
guohai 18414 0.0 1.0 126088 5208 ? S 15:09 0:00 php-fpm: pool www

所以这两个诡异的问题都是权限问题

也就是我们这里Nginx和PHP的运行用户都是guohai,文件所属也都是guohai,这样就不会出任何问题了(Nginx或者PHP的master process都是运行在daemon当中,所以这里是root),当然可能正式服务器上应该部署在www这种权限比较少的用户下面。

我这里是迁移工作,所以数据库和程序文件我都有导入出来,现在只需要导入数据库,把文件放到指定的目录就好。如果是全新安装的话,只要创建好数据库和用户就好,安装WP的时候会自动生成数据库。
创建数据库和用户

CREATE DATABASE wp_db;
CREATE USER wp_user@localhost;
SET PASSWORD FOR wp_user@localhost= PASSWORD("YOUR_STRONG_PASSWORD");
GRANT ALL PRIVILEGES ON wp_db.* TO wp_user@localhost IDENTIFIED BY 'YOUR_STRONG_PASSWORD';
FLUSH PRIVILEGES;

当然你如果熟悉MySQL的话,可以细分权限,给够就行了。

https://www.digitalocean.com/community/articles/how-to-install-wordpress-with-nginx-on-centos-6–2

WordPress在Nginx上开启固定链接(permalinks),参见的是http://blog.dighost.me/archives/3233.html,网络上很多方法都不成功,不知道到底是对视错,我的是WP放在子目录(lifelog)下面,所以

    location / {
        index index.php  index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
#       try_files and rewrite, which is better?
#        if (-f $request_filename/index.html) {
#            rewrite (.*) $1/index.html break;
#        }
#        if (-f $request_filename/index.php) {
#            rewrite (.*) $1/index.php;
#        }
#        if (!-f $request_filename) {
#            rewrite (.*) /index.php;
#        }
    }

    location /lifelog/ {
        index index.php  index.html index.htm;
        try_files $uri $uri/ /lifelog/index.php?$args;
    }

也就是我只修改了location /lifelog/的内容,并且index后面有这么多,部分人只放了index.php,但是我这样就会出现404,如果写全就能工作,所以。。。

所有东西都弄好之后,然后可以隐藏掉Nginx/PHP版本号,避免软件漏洞出现后,攻击者很容易知道你的系统软件版本,然后直接拿下你的机器,不过我这台机器应该没有人感兴趣。
参见http://wangye.org/blog/archives/352/
分别在/etc/nginx/nginx.conf增加
http {
# …省略一些配置
server_tokens off;
}

在/etc/php.ini关闭expose_php,即修改为
expose_php = Off
或者你自己编译源码,尽量隐藏掉你不想让别人看到的信息。

P.S.
运行了几个小时,发现有时候还是会有404,先用用看有没有什么比较严重的问题,备份很重要,有空来写几个脚本去定时导出和打包数据库和文件。
需要注册的同学可以使用https://www.digitalocean.com/?refcode=7340d6d347a6这个优惠码,你好我也好,哈哈哈 /偷笑

跑了会儿就觉得站点反应慢,看了下
top发现CPU很空,Memory几乎满掉了
ps -A –sort -rss -o comm,pmem,pcpu | uniq -c | head -15
发现php-fpm占了很多内存
ps aux | grep php-fpm
网上搜了下,遇到php-fpm开的多的人很多,而每个php-fpm进程大约占用20M左右的内存,所以内存很快就会耗光了
http://www.perfgeeks.com/?p=599
http://www.ha97.com/4339.html
这两篇博客解释了php-fpm的工作方式,我这里是动态的,然后我这里VPS的内存比较小,而max_spare_servers默认是35个,所以就会耗非常多的内存,于是我尝试将这个值改小。
vim /etc/php-fpm.d/www.conf
修改pm.max_spare_servers
再跑跑看。

P.P.S. 难道为了搞个VPS搭个小站,还要逼我学习如何调优Nginx/PHP/MySQL?突然发现还是我那个40人民币/年的空间比较好,ping值也低,也没有这么多事,可惜它经常不出声响的就处于维护状态,让人比较头疼。用了这个VPS,现在如果坏了得自己修了。。。

P.P.P.S 发现这事停不下来了
一些Nginx简单的配置
vim /etc/nginx/nginx.conf

Nginx上开启gzip选项

    gzip  on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types text/plain application/x-javascript text/css application/xml;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";

可以用curl命令测试下是否设置生效

curl -I guoh.org/lifelog/ -H Accept-Encoding:gzip,defalte // 指定Head接收编码信息
curl -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -I guoh.org/lifelog/ -H Accept-Encoding:gzip,defalte // 指定代理为某种浏览器,可以用任意值来"欺骗"服务器
curl -A "Mozilla/4.73 [en] (X11; U; Linux 2.2.15 i686)" -I guoh.org/lifelog/ -H Accept-Encoding:gzip,defalte

配置静态文件超时时间(这段静态文件超时的配置文件是错的,参见2015/01/25 UPDATE部分)

    location ~* .(ico|gif|bmp|jpg|jpeg|png|swf|js|css|mp3) {
        expires 30d;
    }

Nginx Proxy Cache如果有需要的话,再来研究。
http段里面添加

proxy_connect_timeout 10;
proxy_read_timeout 180;
proxy_send_timeout 5;
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
proxy_temp_path /tmp/temp_dir;
proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache_one:100m inactive=1d max_size=10g;

server段里面添加

location ~ .*\.(gif|jpg|png|css|js)(.*) {
     proxy_pass http://appserver ;
     proxy_redirect off;
     proxy_set_header Host $host;
     proxy_cache cache_one;
     proxy_cache_valid 200 302 24h;
     proxy_cache_valid 301 30d;
     proxy_cache_valid any 5m;
     expires 90d;
}

WP插件有关
顺手把SyntaxHighlighter做了些调整,选用第三版,调整了下它的隔行变色以及高亮行颜色
vim lifelog/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/styles/shThemeDefault.css

.syntaxhighlighter .line.alt1 {
  background-color: white !important;
}
.syntaxhighlighter .line.alt2 {
  background-color: #EEE !important;
}
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
  background-color: #FFFF00 !important;
}

2015/01/25 UPDATE:

2015/01/25 11:01:17 [error] 1265#0: *5 "/path/to/guoh.org/lifelog/2013/09/nanojpeg-a-compact-jpeg-decoder/index.html" is not found (2: No such file or directory), client: xxx.xxx.xxx.xxx, server: guoh.org, request: "GET /lifelog/2013/09/nanojpeg-a-compact-jpeg-decoder/ HTTP/1.1", host: "guoh.org", referrer: "http://guoh.org/lifelog/?s=jpeg"

发现博客有几篇文章一直报这个问题,以前一直都不知道什么原因,没啥时间来研究这个,于是一放就放了一年多,前几天把WP升级了下(怀着侥幸的心理会修复这个bug,并且似乎升级之后第一次真的能访问或者还是我幻觉了,总之就没管了),今天准备来修复另外一个问题,但是又发现问题复现了,看了会儿不知道是脑子哪根线路连上了,突然怀疑到是URL当中包含jpeg就会出现这个问题,于是试了下果真包含jpeg的就有问题,果断猜想配置文件写的有问题,搜了下配置文件当中包含jpeg的部分,就只有静态文件超时配置的部分包含jpeg,于是修改了下,发现问题消失了,试了多次确认就是这里。正确的配置如下(注意和原来的差别):

    location ~ .*\.(ico|gif|bmp|jpg|jpeg|png|swf|js|css|mp3) {
        expires 30d;
    }

Lesson Learn之文件被mv给覆盖

昨天晚上发生了一件悲剧的事情,写了一下午的代码在mv过程当中被删除了。事情经过是这样子的,创建好Git仓库之后,准备把代码文件mv进对应的文件夹,原本目录的样子

$ tree
.
|-- a
|-- a.c

就这两个简单的文件,所以我直接用

$ mv a*

大家可以看到这个命令没有敲完,因为后面还缺destination directory,然后我就切换到那个目录看了一眼路径,回来的时候鬼使神差的没有把路径敲上去就让这个命令执行了,然后惨剧就发生了,这个命令没有出错,我有种预感出事情了,但是这个时候心理还是期望a.c把a给覆盖了,但是ls看了下文件大小就觉得完了,源文件被覆盖了,因为mv没有撤回的命令,所以知道麻烦来了,网络上有人说这个时候可以umount被删文件所在的分区,然后用root用户对该分区进行grep,当然关键字就是被删文件当中有的关键字,越特殊越好,我有试过这方法,但是运气没那么好,只搜出来那个文件的名字,没有内容。想想算了,自己再重写一遍吧,源文件生成的可执行程序还在,里面还有些打印的文本信息,根据这些文本信息来重新写也还行,然后花了2个多小时,到凌晨2点,终于算写完了,功能也和侥幸留下的那个可执行文件一样。于是就睡觉了。

但是我在想mv是如何解析”a*”这个字串,然后把文件覆盖掉的呢?
早上起来看了下mv程序的代码

n_files = argc - optind; // optind是排除前面program name和options之后的参数索引,
						 // 也就是被操作的文件或者文件夹
file = argv + optind;

我们都知道argv[0]是program name,所以”a*”这种情况optind就为1,于是

$ mv a*

就被解释成了

$ mv a a.c

所以当然a.c就被a给覆盖了

那为什么mv在覆盖的时候没有给予提示,因为在Linux/Unix的世界里面,它默认你应该知道你在执行什么操作。比如这个命令,移动A文件到B文件,这是再正常正确不过的一种操作,它就是认为你是要用A把B给覆盖的。

当然”a*”变成a和a.c这纯粹是一种巧合,因为一旦你要操作的文件(夹)数目大于2,它就会去检测最后一个是否是文件夹,如果是的,就把前面的文件(夹)移动到最后一个文件夹里面,如果不是,就告诉你输入错了。

if (target_directory_operand (file[n_files - 1]))
	target_directory = file[--n_files];
else if (2 < n_files)
	error (EXIT_FAILURE, 0, _("target %s is not a directory"),
			quote (file[n_files - 1]));

if (target_directory)
{
	int i;

	ok = true;
	for (i = 0; i < n_files; ++i)
		ok &= movefile (file[i], target_directory, true, &x);
}
else
	ok = movefile (file[0], file[1], false, &x);

所以如果我当前目录有2个以上的a打头的文件,那么我执行”mv a*”,它就会给出提示,或者只是说把文件挪动了一个位置,并不会覆盖。

好了,如果你担心以后在执行cp和mv的时候文件会给覆盖的话,你可以加”-i”参数,这样碰到覆盖的情况它就会征求你的意见,当然我们这么做只是为了避免像如上这个例子的巧合和意外。你始终还是要知道你在执行什么操作。

方便起见,你可以在.bashrc当中添加如下配置就可以。

# some interactive reminders
alias mv='mv -i'
alias cp='cp -i'

为什么rm不加,因为你要知道rm就是要把你的资料弄没的,所以要万分小心!

当然如果你知道你要用VCS来管理你的东西,那么一开始就用吧!

Android标记贴

这里记录些经常遇到的有关Android/AOSP/PandaBoard的问题(可能比较小,并且又不知道放哪里的事情)!

1. PandaBoard获取root privilege(adb remount)

/path/to/aosp/out/target/product/panda/root/default.prop

修改设定为ro.secure=0
再次打包镜像,理论上这应该对所有的ROM都管用!
当然我这里是AOSP源码编译然后烧录的,如果只是普通的ROM,想root的话,请参考官方或者网络上的方法!

HTC刷机之重写CID

弄来台新的手机,没想到有些ROM在这台机器上不能用(平时用的机器大多是什么ROM都可以刷的)。

拿到手后就直接fastboot刷ROM,发现提示“remote:42 custom id check fail”
知道原因,但是不知道怎么解

后来无意中看见这台机器的CID是有限定的,大概就是大陆地区专用,非那种常用的Super CID,即11111111

于是就想把CID改掉,搜索了下,步骤很简单

先进入bootloader
power off[unplug battery] -> volume down + power key或者adb reboot-bootloader

执行

fastboot oem writecid 11111111

直到提示OKAY说明成功

然后再执行

fastboot reboot-bootloader

就可以看到修改过的CID

或者
执行

fastboot getvar cid

查看CID

完整的log如下

username@KNIGHT:~$ adb reboot-bootloader
username@KNIGHT:~$ fastboot oem writecid 11111111
...
省略了很多log
...
(bootloader) writecid: successfully
OKAY [ 22.098s]
finished. total time: 22.098s
 
username@KNIGHT:~$ fastboot reboot-bootloader
rebooting into bootloader...
OKAY [  0.150s]
finished. total time: 0.150s

username@KNIGHT:~$ fastboot getvar cid
cid: 11111111
finished. total time: 0.000s

CID就是Customer IDentity,其用处通俗来讲就是用来区别这台机器是出货到哪个地区的,更多信息大家可以Google。

如果不知道fastboot以及adb这些是什么东西的话,建议先Google了解下。

另外机器必须是S-OFF,这个大家去想办法

另外解锁bootloader很简单,目前官方已经提供,照着做就可以
详见http://www.htcdev.com/bootloader/

修改好了这些之后大家就随便刷吧,有时候可能要分两次刷,先pre-flash比如hboot,再刷其他的,反正照着提示再刷一次就可以了
最后刷机有风险,所列只是自己的记录,不保证所有人都是可以正常执行这些操作的

吐槽公司读书活动

吐槽文章一篇

如今在很多公司流行小组内的读书活动,这件事情本身的意图是好的,但是因为进行的实际情况有差别,所以最终并没有什么明显的效果,只是在完成老板的任务,在浪费大家的时间。

今天就来吐槽吐槽。因为公司的定位不一样,比如有的以技术为导向的公司,有着工程师文化,员工对技术的热爱以及尊重比较浓厚点;有的纯粹以盈利为目的公司,看重的就是交付,出货,对技术的热爱就不是那么明显,员工只要完成任务,按照某些guideline完成任务,很少或者不思考为什么,这样的公司每位员工之间的区别就比较明显一点。当然不是说都要大家热爱技术胜过一切,每个人都有每个人的生活方式,这个是自己选择的。

但是在软件公司,靠程序换金钱的公司,即使如今写程序变得傻瓜了很多,COPY&PASTE也能完成任务,但是有时候还是需要我们思考,还是会遇到问题,还是要我们解决,如果你技术懂得多点,原理了解的多点,解决起来可能就更容易点,花的时间就更少点(这样你就有更多的时间自己掌控,比如按时下班回家,不要以为苦逼的在那里因为自己完成事情慢而加班是件很光荣的事情)。

这是很容易理解的,在计算机世界总是有规律的,不会无中生有,所有事情的发生都是有确定的原因的。你对它了解的多,就是所谓的技术强,反之亦然。本人也不是什么技术牛人,只是比较喜欢探寻这些东西罢了,就跟人喜欢打牌娱乐是一样的。

说了一大串,正式进入主题,为什么要吐槽这么“有意义”的活动。试想我们为什么要读书?我们读书是为了理解和掌握更多的知识。很多人平时就没有看过读书的内容,包括每期主讲书本内容的人也没有很认真的看过,没有对一些重要的东西理解,深入,归纳,只是在照着书念。很明显,一本写的好的书,书本的内容应该都还是比较好理解的,所以不用大家在一起读书自己看看也能明白,这种读书就真的是在“读”书,大家没有什么疑问(不知道怎么提出疑问,或者不好意思提出疑问),差不多要阅读的内容翻完,时间也差不多到了,完美的交差。

但是反过来想想这有什么意义?开个茶话会,发发呆都比这好玩,再说还占用了自己和家庭生活的时间。那么我们该怎么做呢?两种方式:一、不进行这样的活动;二、认真精心组织和开展这样的活动。第一点就不说了,主要说第二点。

对于组织者,即每期的主讲人一定需要对所讲的内容比较熟悉,要先做过学习,归纳整理出自己理解,有条理的罗列出来。这里大概也能用“八二”法则,比如你自己懂的内容为十成,有可能讲出来别人只能吸收你的二成,如果自己都没有做过学习,没有看过,何来讲给别人听呢,让别人明白呢?讲解某个东西一定需要用比较浅显易懂的方式,示意图,类比这些都可以用上,想想讲解的时候一直再说些看似很高级的词汇,不把最重要的点穿,这个又有什么用呢?

对于参与者,你需要拥有能理解所讲的内容的基本能力,如果对于你来说别人讲的都是天书,完全不懂,我想也没有必要参加这样的活动。

最终这种活动期望的目的应该是不管对于主讲者还是参与者都应该有所收获,对知识有进一步的理解,这样对我们来说花得这种时间和力气才是值得的。如果不想参加这种活动,可以选择不参加,不要搞得像完成任务,记录工作考绩一样,真的没有什么必要。想想你做到了吗?