鉴于某些众所周知的原因,GitHub传输速度太拉,gitee又半死不活的,于是决定给自己搭个本地git仓库用,一方面解决了公私有代码的问题 反正都是私有了,另一方面上传下载快的1p,还可以选择自己喜欢的管理风格。然后简单整理了一下现有的开源git服务器。只能说这玩意一团散沙,不靠友商对比都不知道有哪些,在这里简单列一下。

  1. ruby系:GitLab。这个没什么好说的,说到私有git服务器第一反应必然是这个,似乎也是目前已知的开源git服务器中体量最大的,功能齐全,界面美观。问题在于自己编linux包麻烦,我omnibus-gitlab整了几天都没编出来= =

  2. golang系:Gogsgiteaforgejo。这仨一脉相传,我是先用的forgejo,然后又回头试用了下gogs,发现两者的html模板几乎都长得一模一样,后知后觉地搜了下才发现的。简单概括来说,就是先有的gogs,然后一群贡献者嫌gogs更新慢、不加新功能,然后fork出了gitea;然后gitea搞了几年之后开公司了,codeberg担心gitea搞收费,就又从gitea代码fork出了一个forgejo自己开发(贵圈真乱.jpg)。但是不得不提的是,gogs的确老了,甚至不支持统计展示当前仓库各个语言的代码量。。

  3. Java系:gitbucketGitBlitOneDev。这三家都是个搞个的,其中OneDev比较新,界面做的也比较现代化。

CAP理论指的是分布式系统通常难以同时达成Consistency一致性(任意规则读写均返回最新数据)、Availability可用性(一个节点挂了不影响其余部分正常服务)和Partition Tolerance可分区性(即使集群被网络切割开来,仍然能正常工作)三个特性。其原因大约可以归纳如下:

  1. CA系统:缺少P,相当于完全不允许节点间出现短时(如网络延迟)或长时(如网络中断)导致的不一致性,与现实条件相悖;
  2. CP系统:缺少A,摆烂,炸了就是炸了,以可用性为代价允许对系统分区(但是通信必须正常以保证一致性);
  3. AP系统:缺少C,还是摆烂,各个节点仅提供尽力服务,不保证数据是最新的。

基于CAP理论,业界主流产品都有自己的设计考量,产生了如下主流副本读写方案:

  1. Quorum(NRW):正如其名,三个参数,一共N个副本,客户端读取时指定R个副本,写入时指定W个副本,只有这些读写操作成功完成才能算读写操作成功。代表产品为DynamoDB、Cassandra,优点为读写并行、延迟较低,缺点为读开销大(R份全部读取成功才行),属于AP系统,容错级别为2F+1(为了容忍F个错误,需要2F+1个副本);
  2. Raft/类Paxos:写leader节点和一定数量的Quorum,如果出错则根据一致性协议重新选举,直至完成写操作。代表产品为Zookeeper、PolarDB,优点为客户端读写流量相对较少,缺点为串行写延迟较高(leader去写Quorum),属于AP系统,容错级别为2F+1;
  3. Replicate:客户端写leader(master),leader把所有副本写完后才算写成功。代表产品为Hadoop、Ceph等,优点为容错级别较高(F+1),缺点为副本完全落盘带来的木板效应,可能由于某个副本延迟过高或者失效而导致客户端卡写IO,显然属于CP系统。

参考:

  1. https://zhuanlan.zhihu.com/p/351979103
  2. 钝角

出于兼容性考虑,还是编译一份32位版的,哪都能用。

装包:

pacman -S mingw-w64-i686-gtk3 pkg-config mingw-w64-i686-amtk mingw-w64-i686-tepl mingw-w64-i686-libpeas mingw-w64-i686-gobject-introspection mingw-w64-i686-gspell mingw-w64-i686-vala mingw-w64-i686-itstool mingw-w64-i686-desktop-file-utils

其中有几个地方要修改一下:

  1. 复制一份pkgconfig的tepl-6.pctepl-5.pc,msys2没有这个包;
  2. meson改了API导致语法不兼容,可以选择装meson的时候指定版本为0.59(参见meson.build),或者把data/meson.build文件第七行删掉。

配置:

git checkout gnome-3-38-fix-build
MSYS2_ENV_CONV_EXCL='PKG_CONFIG_PATH' meson

其中MSYS2_ENV_CONV_EXCL是针对MSYS2会自动改变环境变量的分隔符而加的。MSYS2装的pkgconfig只认冒号分隔符,但是MSYS2会自动转成分号,这样pkgconfig就只认PKG_CONFIG_DIR中的最后一个,导致pc文件找不到。

编译完成之后,在MSYS里设置好XDG_DATA_DIRS就可以使用了。界面和Linux下完全一样,win的标题栏都没了。然而非常诡异的一点是,如果试图把dll文件复制到gedit一起打包的话,会发现根本启动不起来,两者只能单独分开,然后通过PATH联系起来,就tm离谱。在3.36版本之前是有win32打包脚本的,不过之后被移除了,可以去git历史里面去看,然而我执行又是各种报错= =

===

第二天更新

又看了眼,发现相比于把dll复制到install目标文件夹里,官方脚本里的配置方式是把gedit install到现有的mingw环境中。那么一种可行的方式是,先用pacman装好需要的库,然后把编译好的gedit install到这个chroot环境中。假设安装到E:\gedit\mingw32目录,那么根据make-gedit-installer脚本,可以有如下操作:

pushd /e/gedit
mkdir -p var/lib/pacman
mkdir -p var/log
mkdir -p tmp
pacman -Syu --root `pwd`
pacman -S mingw-w64-i686-gtk3 ...  # 后面的库略

这个地方的mingw32文件夹是不可少的,主要是因为msys2为了做环境隔离,设立了多个子文件夹。后续可以把这个mingw32文件夹单独提出来,重命名后自由使用。

还有一种偷懒的方法是,先直接用pacman装gedit,再卸掉这个包,剩下来的就是依赖环境了

然后再用meson去reconfigure gedit,指定prefix为E:\\gedit\\mingw32,重新执行ninjaninja install即可完成安装。安装完大小接近1个G,如果需要精简空间,可以参考remove_useless_stuff函数,改改路径前缀执行,最终能把大小控制在400MB以内。(经典的gedit 2.30展开安装也才80MB左右,通货膨胀严重啊)

因为这里不打算发行,就不按照原始脚本用wix进行后续打包工作了。

最近写CMU15-445的时候,project 1里面有个任务类似于写unique_ptr智能指针PageGuard,创建时增加引用计数,销毁时减少引用计数。然后这里有个让人疑惑的地方,就是他要求写移动构造函数。当时我想着,这跟用= default应该没啥区别,后来不放心,还是做了个实验验证下,发现和想象中的还是有些出入。

废话不多说,直接贴代码:

#include <bits/stdc++.h>

using namespace std;

struct A {
    int val = 0;
    string msg = "";
};

int main()
{
    A a1{114514, "1919810"};
    printf("a1{%d,%s}\n", a1.val, a1.msg.c_str());

    A a2(std::move(a1));
    printf("a2{%d,%s}\n", a2.val, a2.msg.c_str());
    printf("a1{%d,%s}\n", a1.val, a1.msg.c_str());

    return 0;
}

然后结果是,a2能成功继承a1的所有数据,但是在move后的a1中,只有std::string的值被清空,而int类型的数据仍然保持了原有值,而非初始化到0。这个结果就与成员变量的平凡性有关了:对于平凡类型,move和直接复制该类型没有任何区别。也就是说,仅仅对于非平凡类型而言,才会存在真实的move动作,并将被move对象重置为设定的某个中间状态——平凡类型要求使用编译器隐式生成的移动构造函数,因而并不会对原有数据进行修改,move之后还是保持原有的值。

由于规定以C语言生成的所有数据结构都是平凡的,我们可以推测指针本身也是一种平凡类型,move后不会被清空。那么这下问题就解决了:要求我们自己实现移动构造函数的原因是,在对象中使用了C指针而非智能指针,并不能通过move的方式来解除被move对象与内存区域的绑定,需要自己手动使用nullptr来重置这些指针,防止它们在预期的生命周期外修改数据,制造未定义行为。

看到MSYS和winlibs两个网站上的gcc都区分了MSVCRT和UCRT版本,这里简单记录一下。

再简单提一下C/C++运行库(C Runtime,CRT)的概念吧,因为所有符合C/C++标准的程序采用的绝大部分基本函数都具有相同的功能(最典型的就是stdio.hmemory.hiostream了),因而将这些常见函数抽象出来做成单独的程序库能够有效节省编译时间,并且对闭源编译器还有隐藏实现的功能等。

然后这里MSVCRT指的是当年Visual C++系列随编译器附带的运行库,也就是说发布一版VC++就会发布一版MSVCRT,那么在动态链接的情况下,目标机器就必须安装对应的运行库。比如说原来最为常见的找不到MSVCP80.dll这类信息,就是因为这种情况产生的,也就是俗称的缺库。

那么UCRT就是微软为了解决这一问题提出的新方案。程序不再链接到某一具体版本的CRT,而是链接到一系列中间dll上,形如api-ms-win-xxxx.dll,想必在某些地方见过。这些dll文件非常小,它们并不包含具体函数的实现,而是作为一个中间层,将对应的函数请求转发到当前系统所支持的CRT中。没有什么是加抽象层解决不了的问题

然后这里区分两种CRT的话,实际上并不只是区分编译出来的程序最终默认链接到哪种CRT,这些gcc本身也会被链接到对应的库。比如说,UCRT版本的gcc会链接到ucrtbase.dll,而MSVCRT版本的会链接到mscvrt.dll

PS1. UCRT版本的gcc包含有MSVCRT的库,但是反过来没有验证是否同样支持; PS2. 在gcc的C支持库中,可以通过传递--with-default-msvcrt=ucrt参数来让MinGW使用UCRT作为其支撑运行库。