群晖DSM 6.2增加zgrep命令

群晖 `DSM 6.2` 系统上执行 `zgrep` 命令的时候提示找不到命令,其他平台系统(`ARM` ),需要手工编译安装`ZUtils`,来提供。但是如果是 `X86` 系统的话(比如 `DS718+` ),可以直接使用交叉编译环境里的工具,直接拷贝到系统即可。

首先参考 搭建群晖交叉编译环境(DS718+/ubuntu 16.04.6/DSM 6.2.1-23824 Update 6) 搭建群晖 `DSM 6.2` 的编译环境。

`DS718+` 执行如下:

$ cd  ~/toolkit//build_env/ds.x64-6.2/

$ scp -r ./bin/zgrep username@10.10.10.111:~/

$ ssh 10.10.10.111 -l username

$ sudo mv ~/zgrep /usr/local/bin/

 

 

需要编译的(`ARM` 芯片版本),执行如下命令编译:

$ cd ~/toolkit/pkgscripts-ng

# 下载 ZUtils 1.8
$ sudo wget http://deb.debian.org/debian/pool/main/z/zutils/zutils_1.8.orig.tar.xz -O ../build_env/ds.x64-6.2/root/zutils_1.8.orig.tar.xz
 
# 也可以本站下载 sudo wget https://www.mobibrw.com/wp-content/uploads/2019/04/zutils_1.8.orig_.tar.xz -O ../build_env/ds.x64-6.2/root/zutils_1.8.orig.tar.xz

$ sudo chroot ../build_env/ds.x64-6.2/
 
$ cd root
 
$ export PREFIX="/root/build_libs"
 
$ export HOST="x86_64-linux-gnu"
 
$ mkdir -p $PREFIX

# 编译 ZUtils 1.8
$ tar xvf zutils_1.8.orig.tar.xz

$ cd zutils-1.8/
 
$ PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig/ \
LD_LIBRARY_PATH=$PREFIX/lib/ \
./configure \
--prefix=$PREFIX
 
$ make clean
 
$ make 
 
$ make install

对于编译好的程序,复杂的可以自己制作安装包,简单的可以直接通过SSH推送到设备的/usr/local/bin目录下即可。

比如本次编译结果:

# 暂时只需要zgrep,因此只安装 zgrep
$ scp -r $PREFIX/bin/zgrep username@10.10.10.111:~/

$ ssh 10.10.10.111 -l username

$ sudo mv ~/zgrep /usr/local/bin/

参考链接


Debian旧版本源同步脚本

最近 `Debian Wheezy/Jessie` 源被官方归档了,使得许多来不及更新系统的服务器没源可用,http://archive.debian.org  访问速度太慢, 不得以自己写个脚本同步 `Wheezy/Jessie` 源。

`Debian` 官方推荐使用 `debmirror` 同步源,但是在群晖等设备上不方便使用,因此还是用` rsync` 同步通用一些。

主要是自己的 `WD MyCloud Gen1` 在编译软件的时候,依赖 `Wheezy` ,`Jessie` 版本的软件包,又没办法升级系统,只能是自己建立软件源,保存一下,免得日后麻烦。

由于 `Debian` 源的结构,要单独分版本来同步很不方便,包都集中在 `/pool` 目录下,故此脚本主要根据索引文件来生成该版本的包列表`ARCH_EXCLUDE` 用来过滤不同架构的包的`dists` 用来过滤不同版本的同步完成后,整个 `Wheezy/Jessie` 源(`i386,  amd64, arm,  source`) 共 96G

`Wheezy Archive` 源设置

deb http://mirrors.163.com/debian-archive/ wheezy main contrib non-free
deb-src http://mirrors.163.com/debian-archive/ wheezy main contrib non-free

同步脚本代码

#!/bin/sh
#author: igi
#date:   2012-03-28

NAME="debian"
SYNC_FILES="$NAME"_files
#TO="/tmp/$NAME"
TO="./$NAME"
FROM="archive.debian.org::debian-archive/debian"
#FROM="debian.ethz.ch::debian-archive/debian"
#FROM="mirror.1und1.de::debian-archive/debian"
#FROM="debian.koyanet.lv::debian-archive/debian"
#RUNDIR="/tmp/"
RUNDIR="./"

ARCH_EXCLUDE="alpha hppa hurd-i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 s390x sparc ppc64el"
for ARCH in $ARCH_EXCLUDE; do
    EXCLUDE=$EXCLUDE"\
        --exclude binary-$ARCH/ \
        --exclude *_$ARCH.deb \
        --exclude *_$ARCH.udeb \
        --exclude installer-$ARCH/ \
        --exclude Contents-$ARCH* "
done

#dists='lenny etch'
dists='wheezy jessie'
for dist in $dists; do
    DIST_INCLUDE=${DIST_INCLUDE}"\
        --include /dists/$dist/ \
        --include /dists/$dist/contrib/ \
        --include /dists/$dist/main/ \
        --include /dists/$dist/non-free/ "
    DIST_EXCLUDE="${DIST_EXCLUDE}\
        --exclude /dists/$dist/* "
done

#sync index files
rsync --progress -av \
    --include 'Packages.gz' \
    --include 'Sources.gz' \
    $DIST_INCLUDE \
    --include '/dists/' \
    --exclude '/*' \
    --exclude '/dists/*' \
    $DIST_EXCLUDE \
    --exclude 'i18n' \
    --exclude 'installer-amd64' \
    --exclude 'installer-i386' \
    --exclude 'installer-armel' \
    --exclude 'installer-arm64' \
    --exclude 'installer-armhf' \
    --exclude 'installer-ppc64el' \
    --exclude 'Release' \
    --exclude '*.bz2' \
    $EXCLUDE \
    "$FROM/" "$TO/"

rm -rf "$RUNDIR"/"$SYNC_FILES"

find "$TO/dists/" -name 'Packages.gz' -exec zgrep 'Filename: ' {} + | awk '{print $2}' >"$RUNDIR"/"$SYNC_FILES"

find "$TO/dists/" -name 'Sources.gz'  -exec zcat {} + | awk '/Package:/{dir="";delete files;next};/Directory:/{dir=$2;next};/Files:/{pass=1;next}; pass && /^ / {files[NR]=$NF}; length(files) && dir && / /{for(i in files) print dir"/"files[i];delete files}; /^[^ ]/{pass=0;next};' >>"$RUNDIR"/"$SYNC_FILES"

#first stage
rsync --progress -v --recursive --times --links --hard-links \
    --files-from="$RUNDIR"/"$SYNC_FILES" \
    "$FROM/" "$TO/"

#second stage
rsync --progress -v --recursive --times --links --hard-links \
    $DIST_INCLUDE \
    --exclude '/dists/*' \
    $EXCLUDE \
    --exclude '/pool/' \
    "$FROM/"  "$TO/"

更多 `debian-archive` 源,见: http://www.debian.org/distrib/archive

参考链接


Debian系统启动非常耗时,屏幕显示“A start job is running for /dev/disk/by-uuid/...a71b-4040-9b53-e92525f6803e”,错误“Timed out waiting for device /dev/disk/by-uuid/197c3d31-a71b-4040-9b53-e92525f6803e”

最近`Debian` 系统启动非常耗时,每次启动都要等待1分30秒的任务超时才能启动,屏幕显示“`A start job is running for /dev/disk/by-uuid/...a71b-4040-9b53-e92525f6803e`”,如下图:

继续阅读Debian系统启动非常耗时,屏幕显示“A start job is running for /dev/disk/by-uuid/...a71b-4040-9b53-e92525f6803e”,错误“Timed out waiting for device /dev/disk/by-uuid/197c3d31-a71b-4040-9b53-e92525f6803e”

Debian:如何从Jessie升级为Buster(Debian 8.0 升级到Debian 10.0)

Debian 10(代号为Buster)已经正式发布,可以开始尝鲜Debian 10了。 需要逐版本升级,不要跨版本升级,目前的尝试来看,跨版本升级会出现问题。

1. 更新Debian Jessie到最新版

$ su

$ apt-get update

$ apt-get upgrade

$ apt-get dist-upgrade

2. 将软件源改为Stretch安装源

$ su

$ sed -i 's/jessie/stretch/g' /etc/apt/sources.list

# 替换成国内镜像源(清华大学)
$ sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list

$ sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn\/debian-security/g' /etc/apt/sources.list

3. 升级系统版本

$ su

$ apt-get update

$ apt-get dist-upgrade

4. 重启

$ su

$ reboot

5. 更新Debian Stretch到最新版

$ su

$ apt-get update

$ apt-get upgrade

$ apt-get dist-upgrade

$ apt-get autoremove

6. 将软件源改为Buster安装源

$ su

$ sed -i 's/stretch/buster/g' /etc/apt/sources.list

# 替换成国内镜像源(清华大学)
$ sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list

$ sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn\/debian-security/g' /etc/apt/sources.list

7. 升级系统版本

$ su

$ apt-get update

$ apt-get dist-upgrade

8. 重启

$ su

$ reboot

9. 清理安装包

$ sudo apt-get autoremove

参考链接


Poppy:开源3D打印人形机器人

由Inria Flower实验室(位于法国波尔多)研究小组创建的 Poppy,是一台经济实惠、易于安装的人形机器人,拥有强大而灵活的硬件配置。使用现成部件(电机及电子元件)和 3D打印的肢体,降低创客自制门槛。

继续阅读Poppy:开源3D打印人形机器人

快速排序算法(QSort,快排)及C语言实现

上节介绍了如何使用起泡排序的思想对无序表中的记录按照一定的规则进行排序,本节再介绍一种排序算法——快速排序算法(Quick Sort)。

C语言中自带函数库中就有快速排序——qsort函数 ,包含在 <stdlib.h> 头文件中。

快速排序算法是在起泡排序的基础上进行改进的一种算法,其实现的基本思想是:通过一次排序将整个无序表分成相互独立的两部分,其中一部分中的数据都比另一部分中包含的数据的值小,然后继续沿用此方法分别对两部分进行同样的操作,直到每一个小部分不可再分,所得到的整个序列就成为了有序序列。

例如,对无序表{49,38,65,97,76,13,27,49}进行快速排序,大致过程为:

  1. 首先从表中选取一个记录的关键字作为分割点(称为“枢轴”或者支点,一般选择第一个关键字),例如选取 49;
  2. 将表格中大于 49 个放置于 49 的右侧,小于 49 的放置于 49 的左侧,假设完成后的无序表为:{27,38,13,49,65,97,76,49}
  3. 以 49 为支点,将整个无序表分割成了两个部分,分别为{27,38,13}{65,97,76,49},继续采用此种方法分别对两个子表进行排序;
  4. 前部分子表以 27 为支点,排序后的子表为{13,27,38},此部分已经有序;后部分子表以 65 为支点,排序后的子表为{49,65,97,76}
  5. 此时前半部分子表中的数据已完成排序;后部分子表继续以 65为支点,将其分割为{49}{97,76},前者不需排序,后者排序后的结果为{76,97}
  6. 通过以上几步的排序,最后由子表{13,27,38}{49}{49}{65}{76,97}构成有序表:{13,27,38,49,49,65,76,97}

整个过程中最重要的是实现第 2 步的分割操作,具体实现过程为:

  • 设置两个指针 low 和 high,分别指向无序表的表头和表尾,如下所示:
  • 先由 high 指针从右往左依次遍历,直到找到一个比 49 小的关键字,所以 high 指针走到 27 的地方停止。找到之后将该关键字同 low 指向的关键字进行互换:
  • 然后指针 low 从左往右依次遍历,直到找到一个比 49 大的关键字为止,所以 low 指针走到 65 的地方停止。同样找到后同 high 指向的关键字进行互换:
  • 指针 high 继续左移,到 13 所在的位置停止(13<49),然后同 low 指向的关键字进行互换:
  • 指针 low 继续右移,到 97 所在的位置停止(97>49),然后同 high 指向的关键字互换位置:
  • 指针 high 继续左移,此时两指针相遇,整个过程结束;

该操作过程的具体实现代码为:

#define MAX 8
typedef struct {
    int key;
}SqNote;

typedef struct {
    SqNote r[MAX];
    int length;
}SqList;
//交换两个记录的位置
void swap(SqNote *a,SqNote *b){
    int key=a->key;
    a->key=b->key;
    b->key=key;
}
//快速排序,分割的过程
int Partition(SqList *L,int low,int high){
    int pivotkey=L->r[low].key;
    //直到两指针相遇,程序结束
    while (low<high) {
        //high指针左移,直至遇到比pivotkey值小的记录,指针停止移动
        while (low<high && L->r[high].key>=pivotkey) {
            high--;
        }
        //交换两指针指向的记录
        swap(&(L->r[low]), &(L->r[high]));
        //low 指针右移,直至遇到比pivotkey值大的记录,指针停止移动
        while (low<high && L->r[low].key<=pivotkey) {
            low++;
        }
        //交换两指针指向的记录
        swap(&(L->r[low]), &(L->r[high]));
    }
    return low;
}

该方法其实还有可以改进的地方:在上边实现分割的过程中,每次交换都将支点记录的值进行移动,而实际上只需在整个过程结束后(low==high),两指针指向的位置就是支点记录的准确位置,所以无需每次都移动支点的位置,最后移动至正确的位置即可。

所以上边的算法还可以改写为:

//此方法中,存储记录的数组中,下标为 0 的位置时空着的,不放任何记录,记录从下标为 1 处开始依次存放
int Partition(SqList *L,int low,int high){
    L->r[0]=L->r[low];
    int pivotkey=L->r[low].key;
    //直到两指针相遇,程序结束
    while (low<high) {
        //high指针左移,直至遇到比pivotkey值小的记录,指针停止移动
        while (low<high && L->r[high].key>=pivotkey) {
            high--;
        }
        //直接将high指向的小于支点的记录移动到low指针的位置。
        L->r[low]=L->r[high];
        //low 指针右移,直至遇到比pivotkey值大的记录,指针停止移动
        while (low<high && L->r[low].key<=pivotkey) {
            low++;
        }
        //直接将low指向的大于支点的记录移动到high指针的位置
        L->r[high]=L->r[low];
    }
    //将支点添加到准确的位置
    L->r[low]=L->r[0];
    return low;
}

快速排序的完整实现代码(C语言)

#include <stdio.h>
#include <stdlib.h>
#define MAX 9
//单个记录的结构体
typedef struct {
    int key;
}SqNote;
//记录表的结构体
typedef struct {
    SqNote r[MAX];
    int length;
}SqList;
//此方法中,存储记录的数组中,下标为 0 的位置时空着的,不放任何记录,记录从下标为 1 处开始依次存放
int Partition(SqList *L,int low,int high){
    L->r[0]=L->r[low];
    int pivotkey=L->r[low].key;
    //直到两指针相遇,程序结束
    while (low<high) {
        //high指针左移,直至遇到比pivotkey值小的记录,指针停止移动
        while (low<high && L->r[high].key>=pivotkey) {
            high--;
        }
        //直接将high指向的小于支点的记录移动到low指针的位置。
        L->r[low]=L->r[high];
        //low 指针右移,直至遇到比pivotkey值大的记录,指针停止移动
        while (low<high && L->r[low].key<=pivotkey) {
            low++;
        }
        //直接将low指向的大于支点的记录移动到high指针的位置
        L->r[high]=L->r[low];
    }
    //将支点添加到准确的位置
    L->r[low]=L->r[0];
    return low;
}
void QSort(SqList *L,int low,int high){
    if (low<high) {
        //找到支点的位置
        int pivotloc=Partition(L, low, high);
        //对支点左侧的子表进行排序
        QSort(L, low, pivotloc-1);
        //对支点右侧的子表进行排序
        QSort(L, pivotloc+1, high);
    }
}
void QuickSort(SqList *L){
    QSort(L, 1,L->length);
}
int main() {
    SqList * L=(SqList*)malloc(sizeof(SqList));
    L->length=8;
    L->r[1].key=49;
    L->r[2].key=38;
    L->r[3].key=65;
    L->r[4].key=97;
    L->r[5].key=76;
    L->r[6].key=13;
    L->r[7].key=27;
    L->r[8].key=49;
    QuickSort(L);
    for (int i=1; i<=L->length; i++) {
        printf("%d ",L->r[i].key);
    }
    return 0;
}

运行结果:

13 27 38 49 49 65 76 97

总结

快速排序算法的时间复杂度为O(nlogn),是所有时间复杂度相同的排序方法中性能最好的排序算法。

参考链接


I2C总线协议图解

I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。

I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信,RS485也为半双工,SPI和UART为双工。

继续阅读I2C总线协议图解