• 任务2:简单手势识别

    |

    金老师给我们本周的任务是做一个手势识别?!

    好吧,我真的对此感到疑惑。到底什么程度才算是“手势识别”?

    在这个项目上花费了将近两周。
    遇到瓶颈时,我便开始怀疑自己是否在这个无底的项目上挥霍了并不充裕的时间。
    直到项目完成的前一刻,我仍禁不住思考这个项目的究竟有何意义。
    不过,好在最终成品里没有一行多余的代码。

    运行屏幕截图

    源码可见于Gitee

    虽然金老师的原话要求使用“Accelerometer和Gyroscope”,我的项目上只用了Accelerometer;
    我觉得我的思路不需要也无法使用到Gyroscope,即使有,也该是用RotationVectorSensor。

  • 任务1.1:编译SensorGraph

    |

    金老师给我们本周的任务编译Android NDK示例中的SensorGraph。如果已经做过任务1的话,这个任务应该会很简单。

    从Github上clone源代码,将SensorGraph项目导入Android Studio,Gradle Sync & Build即可。

    运行屏幕截图
  • 任务1:Android与交叉编译

    |

    金老师这周给我们布置的任务是:“交叉编译一个共享库(Shared Object),在安卓手机中加载它,显示一个Hello, world。”

    要实现这一个目标有很多种途径,最简单的应该算是使用Android Studio中集成好的NDK。Android开发基本上离不开Android Studio。

    NDK是额外组件,需要通过Android Studio中的SDK Manager进行安装。安装时还可以勾选构建工具CMake和调试用的LLDB。

    安装完所有需要的组件后就万事俱备了。凭空将C++引入Android开发,最简单的方法就是从Native C++模板新建Android项目。

    不过,从Native C++模板新建的项目有可能会默认选择其他NDK版本而拒绝使用当前的安装的NDK。这时,在模块的build.gradle配置文件中将NDK的版本指定为当前安装的版本即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    apply plugin: 'com.android.application'

    android {
    compileSdkVersion 28
    defaultConfig {
    // ...
    }

    buildTypes {
    // ...
    }

    // 在这里指定NDK版本。
    ndkVersion "21.0.6113669"

    externalNativeBuild {
    cmake {
    path "src/main/cpp/CMakeLists.txt"
    version "3.10.2"
    }
    }
    }

    dependencies {
    // ...
    }

    模板已经将Gradle所需的全部依赖完整配置好了,并自动在MainActivity中引用了一个native String stringFromJNI()方法。直接修改这个方法的实现就算是完成任务了。

    这里也可以再多做一层封装:

    在Project视图下的app/cpp里新建先定义C++头文件hello.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #ifndef HI_NDK_HELLO_H
    #define HI_NDK_HELLO_H

    #include <string>
    using std::string;

    /**
    * 用英语和中文说“世界你好。”
    * @return "Hello, world! 世界你好!"
    */
    string sayHi();

    #endif //HI_NDK_HELLO_H

    编写对应的C++实现文件hello.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include "hello.h"
    #include <android/log.h>
    using namespace std;

    /**
    * 用英语和中文说世界你好,以期检验JNI中的编码问题。
    * @return "Hello, world! 世界你好!"
    */
    string sayHi() {
    string greeting = string("Hello, world! 世界你好!");

    // 在Logcat中可以看到输出。
    __android_log_write(android_LogPriority::ANDROID_LOG_INFO, "Hi", greeting.c_str());
    return greeting;
    }

    并将上述.cpp文件加入CMakeLists.txtadd_library处理器中,跟模板生成的native-lib.cpp放在一起。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # CMakeLists.txt
    # ...

    add_library( # Sets the name of the library.
    native-lib

    # Sets the library as a shared library.
    SHARED

    # Provides a relative path to your source file(s).
    native-lib.cpp hello.cpp )

    最后在native-lib.cpp中调用新建的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include "hello.h"
    #include <jni.h>
    #include <string>

    extern "C"
    JNIEXPORT jstring JNICALL
    Java_net_qfstudio_hindk_MainActivity_getGreeting(JNIEnv *env, jobject thiz) {
    return env->NewStringUTF(sayHi().c_str());
    }

    执行Gradle Build并连接手机或模拟器运行,即可在Logcat和MainActivity屏幕中看到预期的输出。

    运行屏幕截图

    完整代码可见于Gitee

  • 任务0.1:编译GCC

    |

    金老师给我们的附加任务是从源代码编译一套GCC。

    GNU网站上有完整的关于如何从源代码编译GCC的指导。GNU网站上将编译和安装GCC的过程划分为6个步骤,我们只需要完成前4个步骤(即到Build这一步)就算是完成任务了。

    我打算在云主机中编译GCC。总的来说,这不算是一件难事。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 在编译前确认系统信息。
    $ uname -r
    3.10.0-1062.12.1.el7.x86_64

    $ gcc --version
    gcc (GCC) 8.3.1 20190311 (Red Hat 8.3.1-3)

    $ cat /proc/cpuinfo
    model name : Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz
    cpu cores : 1

    取得GCC的源代码

    这一部分对应指南中的第2步

    我跳过了指南中的第1步,因为第1步描述的是编译GCC所需要安装的软件。这些软件如果缺失的话,可以在“配置”这一步骤中检测出来。

    GCC的源代码可以从GNU的官方Git仓库中克隆下来,或者从镜像网站下载。这里我选择的是速度较快的日本镜像站ftp.tsukuba.wide.ad.jp,下载的版本是9.2.0。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    cd ~
    mkdir -p esit-workspace/task0.1
    cd esit-workspace/task0.1

    wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-9.2.0/gcc-9.2.0.tar.gz
    # 下载时如果速度太慢或失败,可以现在网络条件好的地方下载好,然后使用`scp`上传至服务器。

    # 从压缩包中解压源代码。
    tar -xf gcc-9.2.0.tar.gz

    这样,GCC的源代码就准备好了。

    配置

    这一部分对应指南中的第3步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    $ pwd
    /home/lightyears/esit-workspace/task0.1
    $ ls
    gcc-9.2.0 gcc-9.2.0.tar.gz

    # 根据指南的要求,新建一个目录用来装编译产物。
    $ mkdir gcc-9.2.0-build
    gcc-9.2.0 gcc-9.2.0-build gcc-9.2.0.tar.gz

    # 运行配置脚本。
    $ cd gcc-9.2.0-build
    $ ../gcc-9.2.0/configure
    configure: error: Building GCC requires GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+.
    Try the --with-gmp, --with-mpfr and/or --with-mpc options to specify
    their locations. Source code for these libraries can be found at
    their respective hosting sites as well as at
    ftp://gcc.gnu.org/pub/gcc/infrastructure/. See also
    http://gcc.gnu.org/install/prerequisites.html for additional info. If
    you obtained GMP, MPFR and/or MPC from a vendor distribution package,
    make sure that you have installed both the libraries and the header
    files. They may be located in separate packages.

    configure在运行的过程中报错,看上去应该是必须的一些软件没有安装。依次执行yum search/info GMPyum search/info MPFRyum search MPCyum info libmpc,发现应该是MPC包没有安装,sudo yum install libmpc将需要的包装好,再次执行配置。

    1
    2
    $ ../gcc-9.2.0/configure
    configure: error: I suspect your system does not have 32-bit development libraries (libc and headers). If you have them, rerun configure with --enable-multilib. If you do not have them, and want to build a 64-bit-only compiler, rerun configure with --disable-multilib.

    由于不需要进行编译32位的程序,因此可以使用--disable-multilib重新配置。

    1
    2
    $ ../gcc-9.2.0/configure
    # 程序运行后退出,MakeFile已经生成好了。

    到这里,这一步骤就顺利结束了。

    编译

    这一部分对应指南中的第4步

    由于编译过程会持续很久,这时可以使用screen这样的终端管理器。这样即使断开SSH连接,编译过程也继续运行。

    1
    2
    3
    screen -S compile-gcc # 新建一个窗口提供给编译gcc使用。
    make -j 2 # 然后等待。
    # 可随时断开SSH连接。

    中途可以重新连接上主机看一下编译过程是否出现差错。

    1
    2
    3
    4
    5
    6
    # 当重新通过SSH连接到主机时,使用screen恢复之前的会话。
    screen -r compile-gcc

    # 顺便一提,在Screen会话中时,按下`Ctrl + A, d`组合键可以从当前会话中分离。
    # (在Screen会话中时,)按下`Ctrl + A, k`可以Kill掉当前会话。
    # (在Screen会话中时,)按下`Ctrl + A, ?`可以显示所有可用的命令。

    在我的主机上,编译过程持续了7个小时。

    编译完成后就可以准备安装了。本来还应该进行测试步骤,但因为测试耗费时间太长,跳过这一步吧。

    为避免覆盖系统的GCC,make install时指定一个目录进行安装。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ screen -r compile-gcc
    $ cd ~/esit-workspace/task0.1/ && ls
    gcc-9.2.0 gcc-9.2.0-build gcc-9.2.0.tar.gz

    # 创建一个目录,下一步`make install`的GCC就装在这个新建的目录里。
    $ mkdir gcc-9.2.0-target && ls
    gcc-9.2.0 gcc-9.2.0-build gcc-9.2.0-target gcc-9.2.0.tar.gz

    $ cd gcc-9.2.0-build
    $ make DESTDIR=/home/lightyears/esit-workspace/task0.1/gcc-9.2.0-target install

    make install结束后用热乎着的编译器编译一个新的Hello, world看看。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $ cd ../gcc-9.2.0-target && pwd
    /home/lightyears/esit-workspace/task0.1/gcc-9.2.0-target

    $ ./usr/local/bin/gcc --version
    gcc (GCC) 9.2.0

    $ nano hello-world.c # 写一个Hello, world。

    $ ./usr/local/bin/gcc -v hello-world.c -o hello-world > compile.log 2>&1

    # 查看一下编译日志。
    $ grep -z "/home/lightyears/esit-workspace/task0.1/gcc-9.2.0-target/" compile.log

    $ ./hello-world
    Hello, world!

    好,大功告成。


    编译产生的中间产物很多,安装完成后就不需要这些文件了。若是磁盘空间紧俏,可以直接删除。

    1
    2
    3
    4
    5
    6
    7
    $ du ~/esit-workspace/task0.1/* -sh
    804M /home/lightyears/esit-workspace/task0.1/gcc-9.2.0
    6.1G /home/lightyears/esit-workspace/task0.1/gcc-9.2.0-build
    1.5G /home/lightyears/esit-workspace/task0.1/gcc-9.2.0-target
    119M /home/lightyears/esit-workspace/task0.1/gcc-9.2.0.tar.gz

    rm -rf ~/esit-workspace/task0.1/gcc-9.2.0-build
  • 任务0:建立开发环境

    |

    受到新冠肺炎的影响,学校禁止学生返校,课程进行远程教学。指导本课程的金老师在周一时和我们在线上通过腾讯课堂进行了第一次课,并让我们在下周搭建好课程所需要的开发环境。

    课程所需的工具之一是GNU Compiler Collection,主要会用到GCC中C语言相关功能。考虑到多数同学使用的Windows操作系统上没有GCC,金老师建议我们使用基于Linux内核的云主机,或者在Windows系统上安装虚拟机。

    我自己的主力操作系统是Windows 10,近几天升级到了Insider测试中的2004版本。私以为,如果只是单纯地需要GCC,除金老师的建议外,还有“在Windows上使用Linux子系统”以及“在Windows上使用MSYS2”两种简单易行的方案。不过,这两种方案很可能会有坑点。

    下面简要记录使用各个方案搭建环境的步骤。

    使用基于linux内核的云主机

    选购云服务器

    阿里云腾讯云都提供基于Linux内核的云主机,并且都向学生提供优惠,基本版的云服务器月付人民币10元以下,提供一个公网IP、1Mbps的公网带宽、2GB内存以及足够大的磁盘空间。

    完成学生认证就可以享受学生优惠了。我自己三年前买过一次,隐约记得需要提交学生证作为认证手段。但现在最新的政策是一定年龄(阿里云24岁,腾讯云25岁)以下就自动获得学生身份,非常便捷。

    金老师建议我们购买的产品应该是云服务器(阿里云称之为“ECS”,请注意不要误选择“轻量应用服务器”),可以选择预装CentOS 7或者是Ubuntu操作系统,购买之后可以通过SSH远程连接到服务器。因为已经在桌面设备上用过Ubuntu了,为了体验不一样的感觉,这里我选择了CentOS 7。

    PS. 下一个CentOS版本,CentOS 8已于2019年底发布,阿里云(腾讯云)不日将提供更新的操作系统以供选择吧。

    连接到云服务器

    购买之后在控制面板上查看服务器的公网IP,将这个IP记录下来,随后可以通过Windows上的SSH客户端工具连接到服务器。SSH客户端工具是Windows 10自带的一项系统功能。打开PowerShell或CMD,输入ssh并回车就能看到SSH所有可以使用的选项。

    如果电脑中提示找不到SSH命令,可以在“添加或删除系统功能”对话框中选中“OpenSSH工具”,让Win10自动安装这个工具。

    除了Windows 10自带的SSH外,Putty也是非常流行的工具。Putty提供了友好的图形界面,并且还能发送自带SSH工具所没有的鼠标消息。

    1
    2
    3
    ssh root@ip-of-your-server # 通过这行指令连接到服务器。
    # 输入正确的密码就可以登录服务器了。
    # 输入密码时,窗口不会回显输入的字符。

    SSH服务器会在客户端进入沉默状态(例如,没有任何键盘输入)后的一段时间内主动断开连接。要想SSH保持连接,可以使用-o ServerAliveInterval=60参数,让SSH工具每隔60秒就发送keep-alive消息到服务器,以保持连接。

    经过上面的步骤应该就能连接到自己的服务器了。

    PS. 阿里云(腾讯云)都有保卫服务器网络安全的“安全组”规则,安全组会默认放行SSH连接所需的22端口。而如果修改了SSH连接的端口,或者因为建立网站需要用到80端口等等,都需要在控制面板的安全组设置中开放相应的端口号,否则会出现无法连接的情况。

    PS2. 在我的笔记本中有Linux常用指令笔记CentOS笔记

    PS3. 鳥哥的Linux私房菜網站是内容很全面的参考站点。(虽然我曾经听身边的一些资深玩家吐槽过同名的实体书,槽点是内容过于陈旧。私以为,作为入门材料,这个网站依然具有很大的参考价值,并且鸟哥也在不断更新网站的内容。其实,孤陋寡闻的我是从鸟哥的网站上得知CentOS 8的消息的……)

    PS4. 如果你觉得PowerShell或CMD的字体太丑,可以在Windows应用商店中搜索并下载Windows Terminal。这个软件提供美观的界面,并且具有标签页功能,可以在一个窗口中创建多个连接。

    配置全新的服务器

    登录自己的服务器之后就可以使用uname -r确认系统的内核版本了。

    1
    2
    3
    4
    5
    $ uname -r
    3.10.0-957.27.2.el7.x86_64 # 这里显示的是内核的版本。

    $ cat /etc/redhat-release
    CentOS Linux release 7.7.1908 (Core) # 这里显示的是CentOS的版本。

    你是不是想知道uname指令为什么要使用-r参数呢?

    在Linux系统中获取关于某个指令的帮助信息有几种方式,其一是在指令的后面附加--help参数,其二是使用man指令查询某个指令的Manual Page。例如,要获得uname指令的man page,可以使用man uname;要获得man指令本身的man page,可以使用man man指令。(并不是所有指令都有--help帮助信息或man page。)

    1
    2
    3
    some-command --help  # 显示某命令的帮助,比Manual Page简短。
    man some-command # 显示某命令的Manual Page。
    info some-command # 类似于Manual Page。

    用最高权限的root账户进行所有的日常操作不是很靠谱,因此接下来要使用useradd创建日常使用的账户。

    1
    2
    3
    4
    5
    useradd lightyears -G wheel
    # 以上命令创建名为lightyear的用户,并将它添加到wheel用户组。
    # wheel用户组是CentOS上一个特殊的用户组,在这个用户组中的用户具有sudo权限。
    # 所谓“具有sudo权限”,就是使用`sudo`指令执行一般用户不能执行的特权操作。
    # 在随后步骤中要进行的“安装系统软件”操作就是特权操作之一。

    随后就可以断开root的SSH连接了。

    1
    2
    history # 断开连线前,查看本次连接中都执行了哪些指令。
    exit

    接下来使用新的用户名和密码连接到服务器。

    1
    ssh lightyears@server-ip

    PS. 或许你已经注意到,在使用新的用户连接到服务器之后,“~”后的符号由“#”改变为了“$”。“$”是一般用户的标志。

    1
    2
    [root@hostname ~]#
    [lightyears@hostname ~]$

    PS2. 使用pwd指令可以显示当前目录;顺带一提,root用户的主目录默认为“/root/”。

    1
    2
    $ pwd
    /home/lightyears

    常用的文件操作有pwdcatecholstouchchmodrmmvcdmkdir等。

    安装gcc

    通过SSH连接到服务器后,就可以使用yum来安装gcc了。Linux系统与Windows系统差异之一是Linux系统主要是通过“包管理器”来安装软件。yum是CentOS 7就是使用的包管理器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ sudo yum update   # 从网络上下载软件数据,这是一个特权指令,因此需要结合sudo使用。
    $ yum search gcc # 以gcc为关键字进行搜索
    # 这时可能会返回大量结果,实际上我们只需要名称“最短”的那个gcc。

    $ yum info gcc # 搜索并显示GCC包的信息
    $ sudo yum install gcc # 安装GCC

    $ gcc --version # 显示gcc的版本号
    gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)

    这样,GCC就安装好了,接下来可以尝试用gcc进行一次编译。但在编译之前要输入源代码。

    nanovi都是Linux上好用的文本编辑器。(nano可以通过yum安装。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    $ cd ~
    $ mkdir -p esit-workspace/task0
    $ cd esit-workspace/task0

    $ nano hello-world.c
    # 在弹出的Nano窗口中编写一个Hello, world程序。

    $ cat hello-world.c
    #include <stdio.h>
    int main()
    {
    printf("%s", "Hello, world!\n");
    }

    $ gcc hello-world.c -o hello-world
    # 如果你不清楚上述命令是在干什么,可以查阅Manual Page。
    # 若要在`man`搜索字符串,可以按下“/”键然后输入要搜索的字符串并回车。
    # 按下“N”或“Shfit + N”可以在跳到下一个或前一个搜索结果。

    $ ./hello-world
    Hello, world!

    启用Software Collections仓库并更新gcc版本

    通过yum info gcc可以看到系统中安装的GCC版本,目前CentOS7的base仓库中的GCC版本为4.8,而GCC 9已经推出。

    可以通过Software Collections软件仓库下载比base仓库更新的软件。

    这里提到的软件仓库对应的英语是repository,从仓库里可以下载其他维护者已经编译好的软件,省去了自行编译软件的麻烦。Software Collections(SCL,又译作“软件选集”)仓库是被CentOS官方所推荐的附加软件库之一,目前由一群软件爱好者维护,可以根据SCL官网上的文档安装这个软件仓库。安装好这个软件仓库后就可以从这个仓库下载软件了。

    值得注意的是,GCC在SCL中被包含在devtoolset中,以“GCC”为名进行搜索是查找不到的。

    下面的命令演示启用SCL仓库并安装GCC 8的过程。

    1
    2
    3
    4
    5
    6
    7
    8
    sudo yum update
    sudo yum install centos-release-scl
    sudo yum install devtoolset-8

    scl enable devtoolset-8 bash
    gcc --verison
    which gcc
    whereis gcc

    上面的scl enable命令会为你新建一个Bash(Shell的一种),并在这个Bash中使用新版的gcc。当你使用exit命令退出这个Bash之后,系统就会使用原本的GCC。

    每次要使用新版的gcc时,都需要使用scl enable devtoolset-8 bash命令来启用GCC。如果想让Shell默认使用新版本的GCC怎么办?你可以在/etc/profile.d新建一个enable-software-collections.sh文件,并放入以下内容:

    提示:修改/etc/profile.d时务必要谨慎。)

    1
    2
    3
    #!/bin/sh

    source scl_source enable devtoolset-8

    /etc/profile.d目录下的脚本会在Shell启动时执行。这样,每个新启动的Shell都会自动执行这样命令,并使用新版的GCC。

    PS. 如果你想尝试默认的bash之外的其他Shell,不妨试试zshfish

    在Windows上使用VirtualBox

    在Windows平台上,VMWare和VirutalBox都是好用的虚拟机软件。VMWare我只用过第12版,作为一款出色的商业软件,它的性能比同时期的VirtualBox要好很多。但我更倾向于使用开放和免费的VirtualBox。

    如果你在Windows平台上启用了Hyper V虚拟化,或许你有VirtualBox不能与Hyper V共存的印象。好消息是,最近发布的6.0版本的VirtualBox已经支持与Hyper V共存;只是当Hyper V启用时,VirtualBox不能嵌套VT-x/AMD-V,这样一来,在虚拟机中运行虚拟机将变得很慢。

    但话说回来,我的“4核2.1GHz AMD FX-7500CPU + 8GB800MHZ内存”的联想小新V1070,运行Hyper V时卡在了Windows的启动界面上的动画都卡,Hyper V虚拟化接口比AMD-V引擎慢至少一百倍。即使Hyper V能与VirtualBox共存,平时我也不会启动它。它,实在是,太慢了。

    得知AMD的CPU没有Android Emulator加速的时候,我明白FX-7500已经被这个时代抛弃了。

    在Windows上使用Linux子系统

    Windows Subsystem for Linux(WSL)组件可以在Windows平台上直接运行Ubuntu等著名的发行版。启用步骤简单,卸载也方便,常见的Linux程序多能在这个平台上运行,值得一试。

    但截止到目前,这个子系统还没有实现SysV消息队列及一些其他原生Linux所具备的功能,因此一些GNU C的程序在这个平台上是跑不起来的。

    在Windows上使用MSYS2

    MSYS2继承MSYS项目,基于Cygwin和MinGW-w64,还可以追溯到原初的mingw项目。它使用Windows NT的API将整套POSIX的接口翻译了过来,因此可以在Windows上使用gcc编译出原生Windows程序。其实Git for Windows也有用到基于MSYS2的工作。

    使用Chocolatey可以很方便地安装MSYS2。

    1
    choco install msys2

    在MSYS2的Shell窗口中搜索和安装gcc的过程如下。

    1
    pacman -Syu gcc
© 2021 - lightyears1998
Powered by Hexo , Theme - Icalm