2022年10月

好耶,是红帽系的openEuler

dnf groupinstall -y "Development Tools"

# 下载源代码
git clone https://github.com/ceph/ceph
cd ceph
git checkout quincy-release
git submodule update --init --recursive

# 编译前准备
# openEuler 22.03的源里没有这两个python包,需要改为手动下载
sed -i -E 's/(.*asyncssh$)/#\1/gm' ceph.spec.in
sed -i -E 's/(.*natsort$)/#\1/gm' ceph.spec.in

# 官方还没有加openEuler的选项,但大概率是兼容的
sed -i -e 's/centos|fedora/centos|openEuler|fedora/g' install-deps.sh

./install-deps.sh

# 装完依赖之后再装dnf里没有的两个包
pip3 install asyncssh natsort

dnf install -y xmlsec1 doxygen ninja-build python3-sphinx
dnf install -y libibverbs-devel openldap-devel lz4-devel expat-devel lttng-ust-devel libbabeltrace-devel libatomic librabbitmq-devel librdkafka-devel java-1.8.0-openjdk-devel

# cmake配置
./do_cmake.sh -DCMAKE_BUILD_TYPE=RelWithDebInfo

# 开始编译
ninja

打RPM包的过程有点曲折,openEuler的维护者不知道在干些什么,去维护ceph.spec而不是ceph.spec.in,搞得我不知道版本更新的时候到底该对着谁的spec改了。。

这里就不理openEuler自己公布的spec文件了,用点取巧的方法,伪装成CentOS试试。

# 制作RPM源码包
./make_srpm.sh

# 展开RPM源码包用于安装
# 注意默认是解压到家目录下的,所以在ceph目录下执行完可能发现没任何变化,回家目录检查下肯定有rpmbuild文件夹了
rpm -i ceph-17.2.5-0.g98318ae89f1.src.rpm

# 安装编译依赖
dnf builddep -y --define 'rhel 7' --spec ~/rpmbuild/SPECS/ceph.spec

# 华子的像素级抄袭功底还是在的,包管理器名字都给你改喽
# 说实话还整个大小写混用让人挺不舒服的。。
sed -i -e 's/^BuildRequires:  redhat-rpm-config$/BuildRequires:  (redhat-rpm-config or openEuler-rpm-config)/gm' ~/rpmbuild/SPECS/ceph.spec
sed -i -E 'N;s/(^%if 0%\{\?rhel} == 8$)(\n%py_byte_compile.*)/\1 || 0%{?openEuler}\2/gm' ~/rpmbuild/SPECS/ceph.spec

# RPM编译
# openEuler上的cmake可执行文件是python脚本,然而很怪,一用rpmbuild,python就会掉PYTHONPATH,找不到cmake
# 伪装成rhel7,走centos的编译流程,但是又绕过软件包版本限制
PYTHONPATH=/usr/local/lib64/python3.9/site-packages rpmbuild -ba ~/rpmbuild/SPECS/ceph.spec --define 'rhel 7'

将编译的ceph RPM包作为repo提供给其他机器使用,参考:https://blog.csdn.net/DeamonXiao/article/details/120879577

dnf install -y createrepo
cd ~/rpmbuild/RPMS
createrepo .

然后另外单独起个后台进程,直接用http提供服务;这里用7000端口,根据需要可以自己调。

python3 -m http.server 7000

至此,一个repo就建立好了。

然后配置服务器使用这个源:

cat << EOF > /etc/yum.repos.d/Ceph-dev.repo
[ceph-dev]
name=Ceph Dev
baseurl=http://<ip-address>:7000/
enabled=1
gpgcheck=0
EOF

# 更新缓存,测试源是否添加成功
dnf makecache

苦逼兼职运维的第六年,已经无力吐槽Ubuntu了= =

症状:在nmtui里激活网卡,提示Could not activate connection: XXX because device is strictly unmanaged.

懒人解决方案:

sudo su -c 'echo ",except:type:ethernet" >> /usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf'
sudo service network-manager restart

原因大致是Ubuntu的NetworkManager默认将以太网卡设置为了不管理模式,因而无法通过nmtui对以太网卡进行开关操作,即使配置了网络设置也不管用。而上面这段except则是反向操作,告诉NetworkManager不要不管理以太网卡。

咕果上翻了半天没找到合适的解决方案,感谢这位知乎网友:https://zhuanlan.zhihu.com/p/514966896

最近突然又想玩本地推流了,转来转去发现还是nginx做推流服务器比较舒服(开源且稍微熟悉一点),考虑到之前试过的别人编译的nginx 1.7.11.3 Gryphon过于老旧,而且卡顿严重,就打算自己编译一份。在这里简单记录一下编译流程。

仔细观察nginx官方提供的windows编译版,发现提供的还是32位程序,而在实际编译中也的确发现部分模块存在32位和64位混编导致失败的情况,因而最好用VS自带的x64_x86环境(Native Tools Command Prompt)启动cmd来尽可能避免编译出错。都什么年代了,还在用传统x86

set PATH=%PATH%;C:\msys64\usr\bin

pacman --noconfirm -S nasm

git clone https://github.com/nginx/nginx
git clone https://github.com/arut/nginx-rtmp-module

cd nginx
mkdir deps

cd deps
git clone https://github.com/PCRE2Project/pcre2
git clone https://github.com/madler/zlib
git clone https://github.com/openssl/openssl

cd ..

bash ./auto/configure --with-cc=cl --with-cpp=cl --with-cc-opt='/MP /wd4456' --with-pcre=deps/pcre2 --prefix= --sbin-path=nginx.exe --with-zlib=deps/zlib --with-openssl=deps/openssl --with-http_stub_status_module --add-module=../nginx-rtmp-module

nmake

编译完把nginx.exe复制到项目根目录下即可使用。

还是不要尝试nmake install了,不然会报出各种奇奇怪怪的错误= =

为了玩玩最近很火的AI作画Stable Diffusion,不得不想办法把我在古台上囤了N多的陈年老数据转移到机械大house上。按照常规操作,较为合理的方法是做出共享文件夹,然后用robocopy来更新具体数据。

然而直接卡在了第一步= =

右键文件夹一看,那个熟悉的“共享”选项卡不见了,接在“常规”后面的就是“安全”。。。

马上某度,发现也有许多人遇到这个问题,大致情况可以归类为两种:

一是注册表损坏,用于展示共享文件夹的COM组件配置丢失,这个需要通过手动设置HKEY_CLASSES_ROOT\Directory\shellex\PropertySheetHandlers\Sharing表项的值为{f81e9010-6ea4-11ce-a7ff-00aa003ca9f6}来修复;

二是系统组件运行异常,主要关注Security Accounts ManagerServer两个服务的运行状态,确保它们处于正常运行状态才能正常显示选项卡。

然而经过确认,我电脑上这两个地方都没问题,重启多次仍然不见问题解决。而后在绝望之中碰巧看到了另一篇博客,发现竟然是三哥干的好事。。。

实际上,在注册表里,除了有启用COM组件的配置之外,还有禁用的配置,比如HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked注册表项。打开一看,上面Sharing的COM CLSID{f81e9010-6ea4-11ce-a7ff-00aa003ca9f6}赫然躺在里面。

WTF??我用的好好的,结果你把他给我禁用了?当场删除了对应的注册表子项,甚至不用重启,许久不见的共享选项卡就回到了属性界面。

忽然想起某乎上的吐槽,阿三工程师的特点之一就是做事自在,指不定哪天心情好就把哪个feature给你改了,但是只要你不问他,他就绝不会跟你提到这个事。

这咖喱味的系统我是一秒也不想多用了.jpg

参考:

  1. https://techcult.com/fix-sharing-tab-is-missing-in-folder-properties/
  2. https://weblog.west-wind.com/posts/2020/Apr/13/Missing-Sharing-Tab-in-Windows-Explorer

最近试图在gem5里做一个数据导出的工作,即在运行过程中记录每次内存访问的时间、位置和数据。

本来是打算当一个单线程来写的,但是考虑到本来跑一个程序用时就长,把关键路径做得尽可能短对工作效率比较友好,就开始考虑把io相关的工作单独提取到一个新线程完成,结果不知不觉地把这个问题演变成一个多线程问题了。

由于涉及到并发编程,原子操作和锁是绕不开的两个概念,一轻一重两大门神守护着多线程环境下的数据安全性,而两者在C++下分别对应有最为常用的头文件,<atomic><mutex>。由于锁的基本使用较为简单,此文主要记录原子类型变量的基本使用方法。

原子类型所要完成的任务是,确保对该类型的指定操作以原子方式完成。至于为什么一开始不把类型设计成原子操作的,这里涉及硬件设计与软件语义之间的抽象层次差异,换句话说,以硬件成本来看,不值得。数据类型难以有个规范,要是为每种数据类型单独设计一条指令,那光是原子操作指令都能上天。VCISC,超复杂指令集的奠基者

于是乎,现有计算机系统采用了折中方案,即硬件提供一些最基本的指令,再使用一些软件手段,以时间/空间为代价,来拓宽原子操作特性的可用范围。以此为契机,出现了<atomic>这种向用户提供软(也可能是硬)原子操作的语言基础库。

原子操作最常见的用法是使用类模板定义对应类型的变量:

std::atomic<int> atomic_int{0};  // use (0) instead of {0} is okay

上面的代码定义了一个原子操作的int类型变量,并且赋初值为0。注意这里用的是括号初始化,而不是等号赋值。这是因为在定义变量时,operator =并不是将值赋予该变量,而是执行对象复制操作,而翻阅文档,有如下定义:

atomic& operator=( const atomic& ) = delete;

也就是说,语言禁止了atomic对象的复制,只能通过值初始化的方式在变量声明时设置初始值。

介绍完初始化操作之后,就是读写操作了。std::atomic提供了两种对原子变量的读写方式,一是像普通变量一样正常使用,二是load/store接口:

std::atomic<int> val{0};
int tmp = val.load();      // equivalent to `int tmp = val;`
val.store(1);              // equivalent to `val = 1;`

虽然说像普通变量一样写很方便,但是真到读代码的时候原子变量和普通变量混在一起还是挺麻烦的。个人还是觉得用显式的load/store比较好。

然后就是大名鼎鼎的复杂原子操作了。复杂原子操作具有共同的特征:RMW(Read-Modify-Write,读-改-写),而CAS(Compare-And-Swap,比较并交换)则是其中最为基础的RMW操作,所有的复杂RMW操作都可以基于CAS操作完成。对于CAS,<atomic>头文件提供了两种相似的实现方案,compare_exchange_weakcompare_exchange_strong。cplusplus对于这两种方法的区别介绍的非常隐晦,只是说strong方法用起来符合直觉,运行结果是标准的CAS;而weak方法用起来性能更好,但是可能出现假阴性的情况(即使compare结果是允许swap的,但还是判定为不允许,走失败逻辑)。

先是compare_exchange_strong的使用样例。

std::atomic<int> val{0};
int prev = val.load();
val.compare_exchange_strong(prev, newval);

这里取原始值为prev,以该值作为CAS的对比值,如果执行compare_exchange_strong的时候读取到的val仍然是该值的话,则执行CAS操作,将val值设置为newval,并给出返回值true;否则不执行CAS操作,反而将prev值设置为最新读取到的val值,并返回false

其次是compare_exchange_weak。由于这里可能出现假阴性的逻辑错误,需要额外对代码做一点修改,来保证代码仍然能完成正确的逻辑。

std::atomic<int> val{0};
int prev = val.load();
while(!val.compare_exchange_weak(prev, newval));

可以看到weak的用法与strong略有差别,这里多套了一层while循环。如前面所说,使用while的目的是对付compare_exchange_weak中即使满足CAS条件,但仍然执行失败流程的情况。这种方式并不适用于顺序敏感型的应用,比如乘加操作,不同线程的操作顺序会导致产生截然不同的输出结果;但是对只需要将数据加入指定结构而不要求顺序的类链表操作的话,这个方法还是具有更高的效率的(因为有神秘的性能优化)。

对于部分常见基础类型,std::atomic预先提供了常见运算方式的原子操作实现,比如原子加fetch_add、原子减fetch_sub,对于char/int及其各种衍生基础类,还提供有原子位或fetch_or/位与fetch_or/位异或fetch_xor等原子逻辑操作的实现。