分类目录归档:Linux

修改版 qbittorrent 封禁迅雷、百度等吸血客户端

写在前面

公网分流是否有必要封禁迅雷,这个问题我自己也没有一个很明确的答案。支持者认为,迅雷对于整体 BT 社区的健康发展完全没有贡献,给他们上传完全是浪费带宽。但是从另外一个角度来说,一个字幕 / Raw 组需要分流人员,是为了让观众能够更快地下载到本组的作品,使用迅雷下载的人也是观众,封禁他们就和建立分流组的初衷相悖。我个人倒是倾向于赞成封禁迅雷,因为从我的经验来看,迅雷的 BT 模块存在严重的问题,其可见进度永远是 0,这样让会让正常的客户端无所适从,无法就当前的 peer 列表情况选择合适的上传策略,从整体上来说,是不利于分享的。

下面进入正题

这篇文章想要介绍的是一个 qbittorrent 的修改版,作者为其加入了自动封禁迅雷的能力,非常适合我的使用情况。原发布帖子在这里,GitHub的代码树则在这里。我推荐使用 v3_3_x 这个 Branch,因为相对全是 bug 的 qb4,3 要稳定很多。

具体编译方面,参照 qb 官方的文档编译即可,记得使用 checkinstall 帮助自己管理软件包。

emmmm,本来还以为可以写很多,结果发现没几句话就写完了。主要目的本来就是为了介绍一下这个修改版,至于是否使用,各位就见仁见智了。

使用 checkinstall 代替 make install 编译安装

在 Linux 上编译安装时,我总会有这样一个感觉,make install 放了很多文件到我的系统里,如果我哪天想把这些东西删掉,该怎么做。有人可能会说,make uninstall 不就好了,但是在绝大多数情况下,并没有人会去写这些卸载程序。

这时候就轮到 checkinstall 这个小程序出场了,它可以跟踪 make install 执行的步骤,并且生成相应的 deb/rpm 包,然后就可以使用包管理器愉快地卸载掉编译安装的程序。

以安装 qbittorrent 3.3.16 为例,简要介绍一下流程。

系统环境

Ubuntu 16.04, qbittorrent 3.3.16

准备

首先需要安装 checkinstall,在 apt 的帮助下,这件事并不难,apt install checkinstall 即可。

安装 qbittorrent

首先按照之前的一篇文章处理一下源码和依赖,之后只需要做一个很小的改动,将 make install 这个命令换成 checkinstall,之后它会自动调用 make install 并进行跟踪,完成安装操作。

之后会让你输入该程序包的描述,照做就好:

root@server:~/Software/qbittorrent-3.3.16# checkinstall

checkinstall 1.6.2, Copyright 2009 Felipe Eduardo Sanchez Diaz Duran
           This software is released under the GNU GPL.


The package documentation directory ./doc-pak does not exist. 
Should I create a default set of package docs?  [y]: y

Preparing package documentation...OK

Please write a description for the package.
End your description with an empty line or EOF.
>> qbittorrent 3.3.16
>> 

*****************************************
**** Debian package creation selected ***
*****************************************

This package will be built according to these values: 

0 -  Maintainer: [ root@ns3284041 ]
1 -  Summary: [ qbittorrent 3.3.16 ]
2 -  Name:    [ qbittorrent ]
3 -  Version: [ 3.3.16 ]
4 -  Release: [ 1 ]
5 -  License: [ GPL ]
6 -  Group:   [ checkinstall ]
7 -  Architecture: [ amd64 ]
8 -  Source location: [ qbittorrent-3.3.16 ]
9 -  Alternate source location: [  ]
10 - Requires: [  ]
11 - Provides: [ qbittorrent ]
12 - Conflicts: [  ]
13 - Replaces: [  ]

Enter a number to change any of them or press ENTER to continue: 

Installing with make install...

然后进行的就是标准的 make install 操作,安装完成之后,会输出一些日志,告诉你可以用 dpkg 将它卸载。

======================== Installation successful ==========================

Copying documentation directory...
./
./Changelog
./COPYING
./README.os2
./AUTHORS
./README.md
./INSTALL
./TODO
./NEWS
./doc/
./doc/qbittorrent-nox.1
./doc/qbittorrent.1

Copying files to the temporary directory...OK

Stripping ELF binaries and libraries...OK

Compressing man pages...OK

Building file list...OK

Building Debian package...OK

Installing Debian package...OK

Erasing temporary files...OK

Writing backup package...OK
OK

Deleting temp dir...OK


**********************************************************************

 Done. The new package has been installed and saved to

 /root/Software/qbittorrent-3.3.16/qbittorrent_3.3.16-1_amd64.deb

 You can remove it from your system anytime using: 

      dpkg -r qbittorrent

**********************************************************************

然后我们来看一看 dpkg 中是否已经有了 qbittorrent

root@server:~/Software/qbittorrent-3.3.16# dpkg -l|grep qbit
ii  qbittorrent                          3.3.16-1                                   amd64        qbittorrent 3.3.16

之后如果要卸载,执行 dpkg -r qbittorrent 即可,干净又方便。

参考资料

https://help.ubuntu.com/community/CheckInstall

其实 Ubuntu 还有两个更激进的工具,AutoDebAutoApt,有兴趣也可以了解一下

 

在 Amazon Linux PV 上编译 Xmrig

准确地说,也是我闲着蛋疼,想试试 AWS 跑 Xmrig 的算力如何。结论说在前面,别指望用 AWS 挖矿赚钱,本都回不了。不过,编译 Xmrig 的过程确实值得记录一下。

首先,AWS 的实例有两种虚拟化形式,一种是 HVM,另一种是 PV,两个都是基于 Xen 的架构,一种比较好理解的方式就是将 HVM 比作 KVM,而 PV 就相当于 OpenVZ,是一种共享内核的架构。

如果要使用一些比较老的实例类型,比如 m1 c1 这样的实例,很可能遇到一种情况,就是他们不支持 HVM 全虚拟化架构,需要使用 PV 半虚拟化,但是问题在于 AWS 现在已经逐步移除对 PV 的支持,仅仅剩下 Amazon Linux 还可以使用这个架构,没有我比较熟悉的 Ubuntu。这给我尝试编译造成了比较大的麻烦。不过好在毛球的文档比较齐全,还是有惊无险地搞定了。

首先处理依赖,Amazon Linux 使用 yum 包管理器,也就是 RHEL/CentOS 用的那款,说实话我不是很喜欢,不过也没办法。此外,虽然使用 yum,其源包库并不是 CentOS 的,这个要注意。

sudo yum install -y epel-release
sudo yum install -y git make cmake gcc gcc-c++ libstdc++-static libmicrohttpd-devel libtool

curl -sSL https://github.com/libuv/libuv/archive/v1.8.0.tar.gz | sudo tar zxfv - -C /usr/local/src
cd /usr/local/src/libuv-1.8.0
sudo sh autogen.sh
sudo ./configure
sudo make -j$(nproc)
sudo make install

Xmrig 的编译需要 libuv-static 这个包,很遗憾 AWS 源中并没有这个包,我们只能选择编译安装,命令如上所示。

之后则是比较通用的 git clone 部分,注意去除作者的强制捐赠选项。

git clone https://github.com/xmrig/xmrig
vi xmrig/src/donate.h
# constexpr const int kDonateLevel = 0;
vi xmrig/src/Options.cpp
# case 1003: /*donate-level*/
# if (arg < 0 || arg > 99)

之后进行编译

cd ~/xmrig
sudo ldconfig
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DUV_LIBRARY=/usr/local/lib/libuv.so
make -j$(nproc)

这样就搞定了,再次重申一次,即使使用最便宜的上一代实例 c1 或者 m1(w/o AES-NI),也不可能回本,挖出的矿最多就是实例费用的一半,所以想发财的人,还是别看这篇文章了。

nginx 监听 IPv6 端口

我一直以为,我的 VPS 配置了 IPv6,在 DNS 记录中添加了 AAAA 解析就可以完成双栈网络的架设,然而在数次测试中,IPv6 地址一直都无法正常解析。现在才发现,nginx 需要调整一些配置才能正常地监听 IPv6 端口。

正常情况下,我们的 nginx.conf 文件中会有这样的两行:

listen 80;
listen 443 ssl http2;

这种情况下,实际上是无法监听 IPv6 端口的,我们需要添加如下两行:

listen [::]:80 ipv6only=on;
listen [::]:443 ipv6only=on ssl http2;

运行 systemctl restart nginx (service nginx restart) 之后,就可以正常监听 IPv4 和 IPv6 的端口了,我们可以用 netstat 来确认一下:

# netstat -nlp | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      9396/nginx.conf 
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      9396/nginx.conf 
tcp6       0      0 :::80                   :::*                    LISTEN      9396/nginx.conf 
tcp6       0      0 :::443                  :::*                    LISTEN      9396/nginx.conf 

之前是没有下面两行监听的,现在有了,说明配置成功。

Update:

在最近的配置中,发现 ipv6only 这个选项是不需要的,根据 nginx 的文档,这个选项默认值就是 on,以及这个选项在整个 nginx 配置文件中只能设置一次,所以如果有多个 vhost,这样设置反而会导致错误。

编译安装 qBittorrent 3.3.16 并处理依赖

写在前面

我想说,开源软件都是垃圾。但是问题在于,很多时候我们并没有比垃圾更好的东西用。此外,本文中处理依赖的方式为权宜之计,因此会给出依赖的下载

qb 自从更新了 4.0,bug 就一天比一天多。最近 PPA 上最新的 4.0.3 版本的 build 在安装之后,除了之前的各种 bug 以外,甚至无法正常启动,好不容易启动了以后,也不能正常地加载种子列表,我实在是不想用了。然而,官方的 PPA build 中并没有老版本的 binary,无奈之下,只好自己编译。

此次,我选择了相对稳定的 3.3.16 版本。

准备

在官方的 SourceForge 页面上获取一份源代码(这里是我自己的缓存),并且解压到某个目录下。

qb 的编译安装需要自己处理 libtorrent 的依赖,关于这点,由于我需要保持和本地 Deluge PPA 版本的兼容,因此我并不愿意选择完全从头编译的方法,只能尝试使用 Deluge PPA 的 libtorrent binary,因此遇到了一些问题。

在这里需要谈一谈 pkg-config 这个组件,由于 Linux 存在编译依赖的问题,在没有包管理器的情况下,如果想要在编译时看到链接库所需要的 CFLAGS,需要通过 pkg-config 这个程序来查看,在编译安装第一步执行的 ./configure 完成的一部分工作就是由 pkg-config 得到这些 FLAGS。在之前说过,我的盒子上已经安装了 libtorrent-rasterbar8,也就是所需要的二进制库已经安装,但是在编译前预配置时仍然返回找不到 libtorrent。经过查询,发现是因为 pkg-config 无法返回 linking 所需的编译器标志,也就是这个 binary 并没有注册到系统。所幸在一个 AskUbuntu 答案中,我发现了 libtorrent-rasterbar-dev 这个包,里面向系统注册了编译所需的头文件,安装之后,就没有出错了。

需要注意的是,仍然需要安装 Deluge PPA 对应的 libtorrent-rasterbar-dev 版本并通过 apt-mark hold 防止升级。

除了 libtorrent,还有其他的几个依赖包,不过都可以通过 apt 完成:

sudo apt-get install build-essential pkg-config automake libtool git
sudo apt-get install libboost-dev libboost-system-dev libboost-chrono-dev libboost-random-dev libssl-dev
sudo apt-get install qtbase5-dev qttools5-dev-tools libqt5svg5-dev

编译

这个相对简单

./configure
make -j$(nproc)
sudo make install

即可

在输出了一大堆日志之后,完成了安装

cd src/ && ( test -e Makefile || /usr/lib/x86_64-linux-gnu/qt5/bin/qmake /root/Software/qbittorrent-3.3.16/src/src.pro QMAKE_LRELEASE= -o Makefile ) && make -f Makefile install
make[1]: Entering directory '/root/Software/qbittorrent-3.3.16/src'
install -m 644 -p /root/Software/qbittorrent-3.3.16/doc/qbittorrent.1 /usr/local/share/man/man1/
install -m 644 -p /root/Software/qbittorrent-3.3.16/src/icons/qbittorrent.desktop /usr/local/share/applications/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/qbittorrent.appdata.xml /usr/local/share/appdata/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/16x16/apps/qbittorrent.png /usr/local/share/icons/hicolor/16x16/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/22x22/apps/qbittorrent.png /usr/local/share/icons/hicolor/22x22/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/24x24/apps/qbittorrent.png /usr/local/share/icons/hicolor/24x24/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/32x32/apps/qbittorrent.png /usr/local/share/icons/hicolor/32x32/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/36x36/apps/qbittorrent.png /usr/local/share/icons/hicolor/36x36/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/48x48/apps/qbittorrent.png /usr/local/share/icons/hicolor/48x48/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/64x64/apps/qbittorrent.png /usr/local/share/icons/hicolor/64x64/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/72x72/apps/qbittorrent.png /usr/local/share/icons/hicolor/72x72/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/96x96/apps/qbittorrent.png /usr/local/share/icons/hicolor/96x96/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/128x128/apps/qbittorrent.png /usr/local/share/icons/hicolor/128x128/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/192x192/apps/qbittorrent.png /usr/local/share/icons/hicolor/192x192/apps/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/16x16/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/16x16/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/22x22/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/22x22/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/24x24/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/24x24/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/32x32/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/32x32/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/36x36/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/36x36/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/48x48/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/48x48/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/64x64/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/64x64/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/72x72/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/72x72/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/96x96/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/96x96/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/128x128/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/128x128/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/192x192/status/qbittorrent-tray.png /usr/local/share/icons/hicolor/192x192/status/
install -m 644 -p /root/Software/qbittorrent-3.3.16/dist/unix/menuicons/128x128/apps/qbittorrent.png /usr/local/share/pixmaps/
install -m 755 -p qbittorrent /usr/local/bin/qbittorrent
strip /usr/local/bin/qbittorrent

Known Issues

降级之后,不用担心配置文件的问题。无论是 apt-get purge(会移除 /etc 下的配置文件),还是 apt-get remove(不移除配置文件)的方式卸载新版本 qb,都不会影响 $HOME 下的用户配置文件,因此种子列表和配置并不会丢失。

降级之后可能发生种子列表 GUI 紊乱的问题,会出现大量的空白,这个把所有的信息都勾上显示出来,再取消即可解决。

所有的种子的分享率限制可能会莫名其妙变成 0,如果发现无法启动种子,一直保持 Completed 状态,可以检查一下种子的分享率限制(不是 qb 全局)。

References

https://github.com/qbittorrent/qBittorrent/wiki/Compiling-qBittorrent-on-Debian-and-Ubuntu

https://askubuntu.com/questions/837960/how-to-install-qbittorrent-in-ubuntu-16-10

https://askubuntu.com/questions/231562/what-is-the-difference-between-apt-get-purge-and-apt-get-remove

https://sourceforge.net/projects/qbittorrent/files/qbittorrent/qbittorrent-3.3.16/

调整 Deluge 使用的 libtorrent-rasterbar 版本以实现双栈 IP 汇报

最近一版的 qBittorrent 更新之后,Deluge 所依赖的 libtorrent 变成了 1.1.5(libtorrent-rasterbar9 1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1),然而 Deluge 的双栈 IP 汇报是一件很玄学的事情,目前只有 libtorrent-rasterbar8 1.0.11-1~xenial~ppa1.1(也就是目前 Deluge PPA 上面的版本)才能实现对 U2 和 CMCT 的双栈 IP 汇报,因此需要做一些调整。

经过查询一些资料,发现 Deluge 通过 python-libtorrent 这个包实现对于 libtorrent 版本的绑定,该软件包的版本决定了底层使用的 libtorrent 版本。

先看一下 1.1.5 版的 libtorrent 安装后,系统上的软件包情况:

$ dpkg -l|grep libtorrent
ii  libtorrent-rasterbar8                1.0.11.1+git20170907.c074e87885-1ppa1~xenial1                amd64        C++ bittorrent library by Rasterbar Software
ii  libtorrent-rasterbar9                1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1 amd64        C++ bittorrent library by Rasterbar Software
ii  python-libtorrent                    1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1 amd64        Python bindings for libtorrent-rasterbar
ii  qbittorrent                          4.0.1.99~201711271728-6211-f977d12~ubuntu16.04.1             amd64        bittorrent client based on libtorrent-rasterbar with a Qt4 GUI

可以看到,python-libtorrent 和 libtorrent-rasterbar9 一样,使用了 1.1.5 的版本,那么如何降级呢?

经过 apt-cache policy <package name> 命令的查询,我们发现其实是可以安装 1.0.11 版本的 python-libtorrent 的:

$ apt-cache policy python-libtorrent
python-libtorrent:
  Installed: 1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1
  Candidate: 1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1
  Version table:
 *** 1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1 100
        100 /var/lib/dpkg/status
     1.0.11-1~xenial~ppa1.1 500
        500 http://ppa.launchpad.net/deluge-team/ppa/ubuntu xenial/main amd64 Packages
     1.0.7-1build1 500
        500 http://mirrors.online.net/ubuntu xenial/universe amd64 Packages

$ apt-cache policy libtorrent-rasterbar8
libtorrent-rasterbar8:
  Installed: 1.0.11.1+git20170907.c074e87885-1ppa1~xenial1
  Candidate: 1.0.11.1+git20170907.c074e87885-1ppa1~xenial1
  Version table:
 *** 1.0.11.1+git20170907.c074e87885-1ppa1~xenial1 100
        100 /var/lib/dpkg/status
     1.0.11-1~xenial~ppa1.1 500
        500 http://ppa.launchpad.net/deluge-team/ppa/ubuntu xenial/main amd64 Packages
     1.0.7-1build1 500
        500 http://mirrors.online.net/ubuntu xenial/universe amd64 Packages

$ apt-cache policy libtorrent-rasterbar9
libtorrent-rasterbar9:
  Installed: 1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1
  Candidate: 1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1
  Version table:
 *** 1.1.5+git20171122.a57ad00e15+patched-configure-1ppa1~xenial1 500
        500 http://ppa.launchpad.net/qbittorrent-team/qbittorrent-stable/ubuntu xenial/main amd64 Packages
        100 /var/lib/dpkg/status

从输出结果中可以看出,python-libtorrent 有三个版本,1.1.5 1.0.11 和 1.0.7,我们在这里需要 1.0.11 的版本。同时,为了保证依赖完整,我们需要安装相同版本的 libtorrent-rasterbar8,这个操作可以通过 apt-get install <package name>=<version name> 的命令完成。

之后,还需要做的一件事是方式 apt-get upgrade 的时候更新了我们已经降级的软件包。这个操作通过 apt-mark hold <package name> 完成,在执行了这个命令以后,我们可以看一下效果:

$ dpkg --get-selections|grep libtorrent
libtorrent-rasterbar8				hold
libtorrent-rasterbar9				install
python-libtorrent				hold

可以发现对应的软件包已经被锁定,重启 deluged 之后也成功降级到了 1.0.11 版本。

综上,解决方案如下:

apt-get install libtorrent-rasterbar8=1.0.11-1~xenial~ppa1.1 python-libtorrent=1.0.11-1~xenial~ppa1.1
apt-mark hold libtorrent-rasterbar8 python-libtorrent
dpkg --get-selections|grep libtorrent

.deb 依赖包下载,以防 Deluge PPA 更新导致方法无效:https://down.gloriousdays.pw/Tools/Deluge-libtorrent-1.0.11.tar.xz

关于 Linux 下 CPU Affinity 的一些发现

超线程是一个相对来说有些争议的技术,在某些任务场景下,可能可以提高系统整体的效率,有些时候则不然,由于任务对同一 CPU 核心资源的争抢,效率反而会下降。因此如何正确地分配任务就是一个需要研究的问题。

通常来说,我们认为在 Windows 系统上,一个物理内核的两个逻辑核心在序号上是连续分配的,比如一个 2C4T 的处理器,Thread 0、1 为同一物理核心,Thread 2、3 为同一物理核心;在 Linux 系统上则是间隔分配,比如同样的 2C4T 处理器,Thread 0、2 为同一物理核心,Thread 1、3 为同一物理核心。但是现在我发现并不是这样。

我们可以执行 for file in /sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list; do echo -n "$file "; cat $file; done |sort -k2 -n 这样一个命令来查看核心的相邻情况。

在我的一台 E3v3 上,结果是这样的:

/sys/devices/system/cpu/cpu0/topology/thread_siblings_list 0-1
/sys/devices/system/cpu/cpu1/topology/thread_siblings_list 0-1
/sys/devices/system/cpu/cpu2/topology/thread_siblings_list 2-3
/sys/devices/system/cpu/cpu3/topology/thread_siblings_list 2-3
/sys/devices/system/cpu/cpu4/topology/thread_siblings_list 4-5
/sys/devices/system/cpu/cpu5/topology/thread_siblings_list 4-5
/sys/devices/system/cpu/cpu6/topology/thread_siblings_list 6-7
/sys/devices/system/cpu/cpu7/topology/thread_siblings_list 6-7

可以发现,分布和 Windows 有些类似。

但是在我的另一台 i3-2130 上,结果却是这样的:

/sys/devices/system/cpu/cpu0/topology/thread_siblings_list 0,2
/sys/devices/system/cpu/cpu2/topology/thread_siblings_list 0,2
/sys/devices/system/cpu/cpu1/topology/thread_siblings_list 1,3
/sys/devices/system/cpu/cpu3/topology/thread_siblings_list 1,3

可以看到是典型的 Linux 分配。

更奇怪的是,这个核心分配并不是稳定的,所以如果有对 CPU 资源要求严格的应用,重启之后需要再次检查逻辑核心的分配,以免效率降低。

为 Linux 添加系统调用

本文实际上是浙江大学操作系统课程实验二的一个记录,所以会有一些很奇怪的操作。

实验目的

添加一个系统调用 mysyscall,获取系统中 Page Fault 的次数。基于 Linux Kernel 4.6 的代码完成。

实验方法

这里需要说明一下的是 include/uapi/asm-generic/unistd.hinclude/linux/syscalls.harch/x86/entry/syscalls/syscall_64.tblarch/x86/entry/syscalls/syscall_32.tbl 的区别。unistd.h 是一个 POSIX 标准的头文件,其中包含了 POSIX 标准的系统调用的函数封装;syscalls.h 是 Linux 系统调用真实的声明文件;syscall_<32/64>.tbl 是对应架构的系统调用列表,这里存在 32 和 64 两个版本是因为 x86 架构有 x86_64 这样的扩展。

因此,如果我们需要在一台 x86_64 架构的 Linux 系统中添加一个系统调用,我们对于以上的文件都需要做出修改。但是需要注意的一点是,他们中的系统调用号不必相同,但是最终在 user mode 中使用该系统调用的时候,使用的应该是对应版本 tbl 中的编号。比如一个 x86_64 架构的 PC,最终调用的编号应该和 syscall_64.tbl 中的编号相同。

接下来我们可以开始添加系统调用了,假设我们系统调用的名字为 mysyscall,实现函数体的原型为 asmlinkage long sys_mysyscall(void);

  1. 在 include/linux/syscalls.h 中写入系统调用函数的声明,注意需要写在最后一行 #endif 之前。比如我们这里就需要加入 asmlinkage long sys_mysyscall(void);
  2. 在 include/uapi/asm-generic/unistd.h 中写入 POSIX 标准通用的系统调用声明,虽然类似于 x86 这样的架构有自己的系统调用表,但是我们应该保持兼容。这里我们需要加入这样的两行:
    #define __NR_mysyscall 292
    __SYSCALL(__NR_mysyscall, sys_mysyscall)
  3. 在 kernel/sys_ni.c 中写入刚才加入的系统调用,这个提供了一个 fallback stub implementation,返回值是 -ENOSYS (Function not implemented.)
    cond_syscall(sys_mysyscall);
  4. 在 arch/x86/entry/syscalls/syscall_64.tbl 中写入刚才添加的系统调用,如果不需要在 x86 和 x86_64 上有不同的表现,则应该将属性定为 common
    340    common     mysyscall    sys_mysyscall
  5. 在 arch/x86/entry/syscalls/syscall_32.tbl 中也加入类似的东西,这是为 i386 架构服务的
    380    i386    mysyscall    sys_mysyscall
  6. 实现 Page Fault 统计
    include/linux/mm.h 文件中声明变量 pfcountextern unsigned long pfcount;
    在进程 task_struct 中增加成员 pf,在 include/linux/sched.h 文件中的 task_struct 结构中添加 pf 字段 unsigned long pf;
    在进程创建过程中将 pf 字段置 0,修改 kernel/fork.c 文件中的 dup_task_struct() 函数:

    static struct task_struct *dup_task_struct(struct task_struct *orig)
    {   …… 
        tsk = alloc_task_struct_node(node); 
        if (!tsk) return NULL;
        ……    
     ++ tsk->pf=0; 
        ……
    }

    arch/x86/mm/fault.c 文件中定义变量 pfcount;并修改 arch/x86/mm/fault.cdo_page_fault() 函数。每次产生缺页中断,do_page_fault() 函数会被调用,pfcount 变量值递增1,记录系统产生缺页次数,current->pf 值递增 1,记录当前进程产生缺页次数:

    …
    ++ unsigned long pfcount;
    __do_page_fault(struct pt_regs *regs, unsigned long error_code)
    {
        …
     ++ pfcount++;
     ++ current->pf++;
        …
    }
  7. sys_mysyscall 的实现
    usr/src/linux-4.6 下建立 mysyscall 文件夹,其中建立三个文件 Makefilemysyscall.cmysyscall.h,分别如下:
    mysyscall.h

    asmlinkage long sys_mysyscall(void);

    mysyscall.c

    #include<linux/kernel.h>
    #include<linux/init.h>
    #include<linux/sched.h>
    #include<linux/syscalls.h>
    #include "mysyscall.h"
    asmlinkage long sys_mysyscall(void)
    {
     printk("Current Page Fault Count: %lu\n", current->pf);
     return 0;
    }

    Makefile

    obj-y:=mysyscall.o
    
  8. 修改主 Makefile 以编译 mysyscall
    找到 usr/src/linux-4.6/Makefile 中这样一行 core -y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/,改为 core -y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ mysyscall/
  9. 编译安装内核
    sudo make bzImage -j $(nproc) && sudo make modules -j $(nproc) && sudo make modules_install && sudo make install && sudo update-grub
  10. 重启
  11. 编写用户态程序,由于我本地是 x86_64 架构,因此使用刚才设定的 340 号系统调用
    #include <stdio.h>
    #include <linux/kernel.h>
    #include <sys/syscall.h>
    #include <unistd.h>
    #define __NR_mysyscall 340
    int main()
    {
      long ret = syscall(__NR_mysyscall);
      printf("Return Value: %ld\n", ret);
      return 0;
    }
    
  12. 在 /var/log/kern.log 中查看输出,或者直接使用 dmesg 查看输出

一些额外的收获

/usr/src/linux-4.6/scripts/checksyscall.sh 是一个很神奇的脚本,它对于所有的代码进行 gcc 预处理之后,判断系统调用是否被正确写入和注册,如果这个测试不能通过,编译成功后也不一定能正常运行该系统调用。

查看 #include 命令调用的真实文件:echo '#include <unistd.h>' | gcc -E -x c -,会得到类似于 # 1 "/usr/include/unistd.h" <some numbers> 这样的输出,意思是后面这些 number 标记的行的代码来自于 /usr/include/unistd.h 

如果要看真实的引用文件,可以这么用:echo '#include <unistd.h>' | gcc -E -x c - | egrep '# [0-9]+ ' | awk '{print $3;}' | sort -u

kern.log 是 Line-Buffered,因此每个 printk 之后需要换行才能在 dmesg 中看到输出

编译内核模块的时候如果发现 arch/x86/Makefile:148: CONFIG_X86_X32 enabled but no binutils support 这样的错误,首先检查一下路径中是否有空格,如果有空格会引发这样的错误

参考资料

Linux Kernel 4.6 代码

https://unix.stackexchange.com/questions/1697/how-can-i-know-which-unistd-h-file-is-loaded

https://www.kernel.org/doc/html/v4.12/process/adding-syscalls.html

http://www.cs.fsu.edu/~cop4610t/lectures/project2/system_calls/system_calls.pdf

https://medium.com/@ssreehari/implementing-a-system-call-in-linux-kernel-4-7-1-6f98250a8c38

https://stackoverflow.com/questions/11237420/error-syscall-function-not-implemented

 

在 Ubuntu 16.04 上编译纯 CPU 挖矿程序

独服的 CPU 闲着也是闲着,小挖怡情,大挖伤身。本文主要基于 NiceHash Miner 可用的挖矿程序,在 Linux 下编译 CPU 可用的版本

Equihash miner for NiceHash

项目源码在这里,这个 miner 的编译稍稍有些麻烦,很多依赖需要自己安装。由于是纯 CPU 挖矿,尽管页面上提到了 CUDA Kit,但是我们并不需要安装这个。

需要的依赖有这些,Boost 1.62+ 和 CMake 3.5+,但是实际操作中,由于 CMake 和 Boost 库存在版本兼容性问题(Boost 1.63 / CMake 3.7+; Boost 1.64 / CMake 3.8+; Boost 1.65 & 1.65.1 / CMake 3.9.3+),因此不能采用简单的 apt 方式安装,而需要自己下载。

Boost 我选择了 1.65.1,具体安装看这里,具体命令如下:

wget https://dl.bintray.com/boostorg/release/1.65.1/source/boost_1_65_1.tar.bz2
tar --bzip2 -xf /path/to/boost_1_65_1.tar.bz2
cd path/to/boost_1_65_1
./bootstrap.sh
./b2 install

然后安装 CMake 3.9.3+,这里我一开始就被坑了,如果不选择合适的 CMake 版本,则编译时无法找到 Boost 库。

wget https://cmake.org/files/v3.9/cmake-3.9.6-Linux-x86_64.sh
chmod +x /path/to/cmake-3.9.6-Linux-x86_64.sh
sudo /path/to/cmake-3.9.6-Linux-x86_64.sh #Set your own install path during the process
sudo ln -s /path/to/cmake-3.9.6-Linux-x86_64/bin/* /usr/local/bin

然后运行 cmake --version 查看版本,应该已经是 3.9.3+ 了

接下来按照 GitHub 页面操作

  1. git clone https://github.com/nicehash/nheqminer.git
  2. 修改根目录下的 CMakeList.txt,修改这样一行: option(USE_CUDA_DJEZO "USE CUDA_DJEZO" ON) 后面的 ON 为 OFF,这是因为我们要纯 CPU 挖矿,不编译 CUDA 版本。
  3. cd nheqminer/cpu_xenoncat/asm_linux/
  4. chmod +x fasm
  5. sh assemble.sh
  6. cd ../../../
  7. mkdir build && cd build
  8. cmake ../nheqminer
  9. make -j $(nproc)

接下来在 build 目录下就会看到编译好的 nheqminer 程序,使用 nheqminer -l equihash.eu.nicehash.com:3357 -u <NH_BTC_ADDRESS>.<worker_name> -t <n_threads> 开始挖矿

CPUMiner-Multi

  1. git clone https://github.com/tpruvot/cpuminer-multi.git
  2. 安装依赖 apt install automake autoconf libcurl4-openssl-dev libjansson-dev libssl-dev
  3. cd cpuminer-multi
  4. ./build.sh
  5. ./cpuminer -a <algo> -o stratum+tcp://<pool_addr> -u <NH_BTC_ADDRESS>.<worker_name> -t <n_threads>

CPUMiner-Multi Help:

Usage: cpuminer-multi [OPTIONS]
Options:
  -a, --algo=ALGO       specify the algorithm to use
                          axiom        Shabal-256 MemoHash
                          bitcore      Timetravel with 10 algos
                          blake        Blake-256 14-rounds (SFR)
                          blakecoin    Blake-256 single sha256 merkle
                          blake2s      Blake2-S (256)
                          bmw          BMW 256
                          c11/flax     C11
                          cryptolight  Cryptonight-light
                          cryptonight  Monero
                          decred       Blake-256 14-rounds 180 bytes
                          dmd-gr       Diamond-Groestl
                          drop         Dropcoin
                          fresh        Fresh
                          groestl      GroestlCoin
                          heavy        Heavy
                          jha          JHA
                          keccak       Keccak
                          luffa        Luffa
                          lyra2re      Lyra2RE
                          lyra2rev2    Lyra2REv2 (Vertcoin)
                          myr-gr       Myriad-Groestl
                          neoscrypt    NeoScrypt(128, 2, 1)
                          nist5        Nist5
                          pluck        Pluck:128 (Supcoin)
                          pentablake   Pentablake
                          quark        Quark
                          qubit        Qubit
                          scrypt       scrypt(1024, 1, 1) (default)
                          scrypt:N     scrypt(N, 1, 1)
                          scrypt-jane:N (with N factor from 4 to 30)
                          shavite3     Shavite3
                          sha256d      SHA-256d
                          sia          Blake2-B
                          sib          X11 + gost (SibCoin)
                          skein        Skein+Sha (Skeincoin)
                          skein2       Double Skein (Woodcoin)
                          s3           S3
                          timetravel   Timetravel (Machinecoin)
                          vanilla      Blake-256 8-rounds
                          x11evo       Permuted x11
                          x11          X11
                          x13          X13
                          x14          X14
                          x15          X15
                          x17          X17
                          xevan        Xevan (BitSend)
                          yescrypt     Yescrypt
                          zr5          ZR5
  -o, --url=URL         URL of mining server
  -O, --userpass=U:P    username:password pair for mining server
  -u, --user=USERNAME   username for mining server
  -p, --pass=PASSWORD   password for mining server
      --cert=FILE       certificate for mining server using SSL
  -x, --proxy=[PROTOCOL://]HOST[:PORT]  connect through a proxy
  -t, --threads=N       number of miner threads (default: number of processors)
  -r, --retries=N       number of times to retry if a network call fails
                          (default: retry indefinitely)
  -R, --retry-pause=N   time to pause between retries, in seconds (default: 30)
      --time-limit=N    maximum time [s] to mine before exiting the program.
  -T, --timeout=N       timeout for long poll and stratum (default: 300 seconds)
  -s, --scantime=N      upper bound on time spent scanning current work when
                          long polling is unavailable, in seconds (default: 5)
      --randomize       Randomize scan range start to reduce duplicates
  -f, --diff-factor     Divide req. difficulty by this factor (std is 1.0)
  -m, --diff-multiplier Multiply difficulty by this factor (std is 1.0)
  -n, --nfactor         neoscrypt N-Factor
      --coinbase-addr=ADDR  payout address for solo mining
      --coinbase-sig=TEXT  data to insert in the coinbase when possible
      --max-log-rate    limit per-core hashrate logs (default: 5s)
      --no-longpoll     disable long polling support
      --no-getwork      disable getwork support
      --no-gbt          disable getblocktemplate support
      --no-stratum      disable X-Stratum support
      --no-extranonce   disable Stratum extranonce support
      --no-redirect     ignore requests to change the URL of the mining server
  -q, --quiet           disable per-thread hashmeter output
      --no-color        disable colored output
  -D, --debug           enable debug output
  -P, --protocol-dump   verbose dump of protocol-level activities
      --hide-diff       Hide submitted block and net difficulty
  -S, --syslog          use system log for output messages
  -B, --background      run the miner in the background
      --benchmark       run in offline benchmark mode
      --cputest         debug hashes from cpu algorithms
      --cpu-affinity    set process affinity to cpu core(s), mask 0x3 for cores 0 and 1
      --cpu-priority    set process priority (default: 0 idle, 2 normal to 5 highest)
  -b, --api-bind        IP/Port for the miner API (default: 127.0.0.1:4048)
      --api-remote      Allow remote control
      --max-temp=N      Only mine if cpu temp is less than specified value (linux)
      --max-rate=N[KMG] Only mine if net hashrate is less than specified value
      --max-diff=N      Only mine if net difficulty is less than specified value
  -c, --config=FILE     load a JSON-format configuration file
  -V, --version         display version information and exit
  -h, --help            display this help text and exit

 

OpenVZ 架构 VPS 配置 IPv6 TunnelBroker

虽然现在 IPv6 尚未完全普及,但是作为一个本地有 IPv6 连接,盒子也是双栈 IP 的用户,双栈 VPS 对我来说也算是一个很有意义的特性。但是很多 Low End VPS 商家售卖的 OpenVZ VPS 并不提供 IPv6 地址,OpenVZ 架构也不像 KVM 一样可以方便地添加 TunnelBroker 实现双栈网络,因此便需要寻找其他的解决方案。

所幸并不是我一个人有这个需求,借助 tb_userspace 这个小工具,可以很快地完成这样的配置工作。记录配置过程如下:

  1. 在 tunnelbroker.net 创建一个新的 Regular Tunnel,取得 Server IPv4 Address 和 Client IPv4 Address 两个配置
  2. 下载 tb_userspace 源代码,编译。此时假设 tb_userspace 存放于 /root目录下
    wget https://down.gloriousdays.pw/Tools/tb_userspace.c
    gcc tb_userspace.c -l pthread -o tb_userspace
  3. 创建自启动脚本 /etc/init.d/ipv6tb
    #! /bin/sh
     
    ### BEGIN INIT INFO
    # Provides:          ipv6
    # Required-Start:    $local_fs $all
    # Required-Stop:     $local_fs $network
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: starts the ipv6 tunnel 
    # Description:       ipv6 tunnel start-stop-daemon
    ### END INIT INFO
     
    # /etc/init.d/ipv6tb
     
    touch /var/lock/ipv6tb
     
    case "$1" in
      start)
        echo "Starting ipv6tb "
          setsid /root/tb_userspace tb <Server IPv4 Address> <Client IPv4 Address> sit > /dev/null 2>&1 &
          sleep 3s #ugly, but doesn't seem to work at startup otherwise
          ifconfig tb up
          ifconfig tb inet6 add <Client IPv6 Address from your Routed /64> ::XXX/128 #Add as many of these as you need from your routed /64 allocation
          ifconfig tb mtu 1480
          route -A inet6 add ::/0 dev tb
          route -A inet6 del ::/0 dev venet0
        ;;
      stop)
        echo "Stopping ipv6tb"
          ifconfig tb down
          route -A inet6 del ::/0 dev tb
          killall tb_userspace
        ;;
      *)
        echo "Usage: /etc/init.d/ipv6tb {start|stop}"
        exit 1
        ;;
    esac
     
    exit 0
    
    
    chmod 0755 /etc/init.d/ipv6tb
    update-rc.d ipv6tb defaults #May cause problems, explain later
  4. Ubuntu 16.04 (systemd) 的额外配置
    如果是使用 Upstart 的 Ubuntu 版本,使用 update-rc.d 就可以创建相应的启动项,但是在某些 Ubuntu 16.04 上,如 HostDare 的 VPS,update-rc.d 在将 SysVinit 脚本做一个 wrapper 以后产生的 Systemd 自启动脚本并不能正常工作。经过排查,发现这个问题产生于 Systemd 脚本的 Type 问题,ipv6tb 应给以 Forking 方式启动,而不是默认的 Simple 模式,如果按照默认模式启动,就会莫名其妙地卡住。因此,如果在 Ubuntu 16.04 上配置,不应该使用 update-rc.d 方式创建 Systemd Wrapped SysVinit 脚本,而应该自己在 /etc/systemd/system 里写一个脚本。

    [Unit]
    Description=ipv6tb
    After=network-online.target
    
    [Service]
    Type=forking
    ExecStart=/usr/bin/sudo /etc/init.d/ipv6tb start
    ExecStop=/usr/bin/sudo /etc/init.d/ipv6tb stop
    Restart=always
    
    [Install]
    WantedBy=multi-user.target

之后执行 systemctl enable ipv6tb.service 就可以正常使用了。