Leo's blog

建议麦当劳

PowerPC is IBM’s architecture and nobody but IBM is using it anymore. IBM have their own build of PyTorch and TensorFlow to powerpc, which called the powerai bundle, however the last update was 4 years ago and it only supports pytorch 1.3.

So I HAVE to build PyTorch myself. Building softwares is fairly easy on Linux, as long as you know to put the right thing in the right place.

I choose 1.12.1 because it seems that it’s the last version that supports cuda 11.2. With some modification to the code, pytorch 2.0.1 seems to compile but eventually fail with an error that I don’t have time to figure out how to solve.

These steps is used inside a cluster that I don’t have sudo access. Running cuda installer will result in segmentation fault but luckily the system admin have cuda 11.2 installed at /usr/local/cuda. The system don’t have CUDNN though, however it is possible to use CUDNN from conda-forge.

  1. install miniforge

  2. create a new mamba environment, with mamba create -n <your_env_name_here> python=3.10 cudatoolkit=11.2 cudnn libopenblas blas cmake ninja

  3. clone PyTorch; git checkout v1.12.1; IGNORE pytorch document that asks you to install mkl.

1
2
3
cd pytorch
git submodule sync
git submodule update --init --recursive
1
2
export CUDA_HOME=/usr/local/cuda
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$CONDA_PREFIX/lib:$LD_LIBRARY_PATH
1
pip install -r requirements.txt
1
2
3
export MAX_JOBS=40 # limits the total number of threads. 
# Each PPC core have 4 hardware threads but will cause too much stress to the file system, so I'm running one thread per core.
python setup.py build
  1. Make sure USE_CUDA is on:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
-- ******** Summary ********
-- General:
-- CMake version : 3.27.8
-- CMake command : $CONDA_PREFIX/bin/cmake
-- System : Linux
-- C++ compiler : /usr/bin/c++
-- C++ compiler id : GNU
-- C++ compiler version : 8.4.1
-- Using ccache if found : ON
-- Found ccache : CCACHE_PROGRAM-NOTFOUND
-- CXX flags : -Wno-deprecated -fvisibility-inlines-hidden -fopenmp -DNDEBUG -DUSE_KINETO -DSYMBOLICATE_MOBILE_DEBUG_HANDLE -DEDGE_PROFILER_USE_KINETO -O2 -fPIC -Wno-narrowing -Wall -Wextra -Werror=return-type -Wno-missing-field-initializers -Wno-type-limits -Wno-array-bounds -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-function -Wno-unused-result -Wno-unused-local-typedefs -Wno-strict-overflow -Wno-strict-aliasing -Wno-error=deprecated-declarations -Wno-stringop-overflow -Wno-psabi -Wno-error=pedantic -Wno-error=redundant-decls -Wno-error=old-style-cast -fdiagnostics-color=always -faligned-new -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-math-errno -fno-trapping-math -Werror=format -Werror=cast-function-type -Wno-stringop-overflow
-- Build type : Release
-- Compile definitions : ONNX_ML=1;ONNXIFI_ENABLE_EXT=1;ONNX_NAMESPACE=onnx_torch;HAVE_MMAP=1;_FILE_OFFSET_BITS=64;HAVE_SHM_OPEN=1;HAVE_SHM_UNLINK=1;HAVE_MALLOC_USABLE_SIZE=1;USE_EXTERNAL_MZCRC;MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
-- CMAKE_PREFIX_PATH : $CONDA_PREFIX/lib/python3.10/site-packages;/usr/local/cuda
-- CMAKE_INSTALL_PREFIX : pytorch/torch
-- USE_GOLD_LINKER : OFF
--
-- TORCH_VERSION : 1.12.0
-- CAFFE2_VERSION : 1.12.0
-- BUILD_CAFFE2 : OFF
-- BUILD_CAFFE2_OPS : OFF
-- BUILD_CAFFE2_MOBILE : OFF
-- BUILD_STATIC_RUNTIME_BENCHMARK: OFF
-- BUILD_TENSOREXPR_BENCHMARK: OFF
-- BUILD_NVFUSER_BENCHMARK: ON
-- BUILD_BINARY : OFF
-- BUILD_CUSTOM_PROTOBUF : ON
-- Link local protobuf : ON
-- BUILD_DOCS : OFF
-- BUILD_PYTHON : True
-- Python version : 3.10.13
-- Python executable : $CONDA_PREFIX/bin/python
-- Pythonlibs version : 3.10.13
-- Python library : $CONDA_PREFIX/lib/libpython3.10.a
-- Python includes : $CONDA_PREFIX/include/python3.10
-- Python site-packages: lib/python3.10/site-packages
-- BUILD_SHARED_LIBS : ON
-- CAFFE2_USE_MSVC_STATIC_RUNTIME : OFF
-- BUILD_TEST : True
-- BUILD_JNI : OFF
-- BUILD_MOBILE_AUTOGRAD : OFF
-- BUILD_LITE_INTERPRETER: OFF
-- INTERN_BUILD_MOBILE :
-- USE_BLAS : 1
-- BLAS : open
-- BLAS_HAS_SBGEMM : 1
-- USE_LAPACK : 1
-- LAPACK : open
-- USE_ASAN : OFF
-- USE_CPP_CODE_COVERAGE : OFF
-- USE_CUDA : ON
-- Split CUDA : OFF
-- CUDA static link : OFF
-- USE_CUDNN : ON
-- USE_EXPERIMENTAL_CUDNN_V8_API: ON
-- CUDA version : 11.2
-- cuDNN version : 8.8.0
-- CUDA root directory : /usr/local/cuda
-- CUDA library : /usr/local/cuda/lib64/stubs/libcuda.so
-- cudart library : /usr/local/cuda/lib64/libcudart.so
-- cublas library : /usr/local/cuda/lib64/libcublas.so
-- cufft library : /usr/local/cuda/lib64/libcufft.so
-- curand library : /usr/local/cuda/lib64/libcurand.so
-- cuDNN library : $CONDA_PREFIX/lib/libcudnn.so
-- nvrtc : /usr/local/cuda/lib64/libnvrtc.so
-- CUDA include path : /usr/local/cuda/include
-- NVCC executable : /usr/local/cuda/bin/nvcc
-- CUDA compiler : /usr/local/cuda/bin/nvcc
-- CUDA flags : -Xfatbin -compress-all -DONNX_NAMESPACE=onnx_torch -gencode arch=compute_70,code=sm_70 -Xcudafe --diag_suppress=cc_clobber_ignored,--diag_suppress=integer_sign_change,--diag_suppress=useless_using_declaration,--diag_suppress=set_but_not_used,--diag_suppress=field_without_dll_interface,--diag_suppress=base_class_has_different_dll_interface,--diag_suppress=dll_interface_conflict_none_assumed,--diag_suppress=dll_interface_conflict_dllexport_assumed,--diag_suppress=implicit_return_from_non_void_function,--diag_suppress=unsigned_compare_with_zero,--diag_suppress=declared_but_not_referenced,--diag_suppress=bad_friend_decl --expt-relaxed-constexpr --expt-extended-lambda -Wno-deprecated-gpu-targets --expt-extended-lambda -DCUDA_HAS_FP16=1 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__
-- CUDA host compiler :
-- CUDA --device-c : OFF
-- USE_TENSORRT : OFF
-- USE_ROCM : OFF
-- USE_EIGEN_FOR_BLAS : ON
-- USE_FBGEMM : OFF
-- USE_FAKELOWP : OFF
-- USE_KINETO : ON
-- USE_FFMPEG : OFF
-- USE_GFLAGS : OFF
-- USE_GLOG : OFF
-- USE_LEVELDB : OFF
-- USE_LITE_PROTO : OFF
-- USE_LMDB : OFF
-- USE_METAL : OFF
-- USE_PYTORCH_METAL : OFF
-- USE_PYTORCH_METAL_EXPORT : OFF
-- USE_MPS : OFF
-- USE_FFTW : OFF
-- USE_MKL : OFF
-- USE_MKLDNN : OFF
-- USE_NCCL : ON
-- USE_SYSTEM_NCCL : OFF
-- USE_NCCL_WITH_UCC : OFF
-- USE_NNPACK : OFF
-- USE_NUMPY : ON
-- USE_OBSERVERS : ON
-- USE_OPENCL : OFF
-- USE_OPENCV : OFF
-- USE_OPENMP : ON
-- USE_TBB : OFF
-- USE_VULKAN : OFF
-- USE_PROF : OFF
-- USE_QNNPACK : OFF
-- USE_PYTORCH_QNNPACK : OFF
-- USE_XNNPACK : OFF
-- USE_REDIS : OFF
-- USE_ROCKSDB : OFF
-- USE_ZMQ : OFF
-- USE_DISTRIBUTED : ON
-- USE_MPI : OFF
-- USE_GLOO : ON
-- USE_GLOO_WITH_OPENSSL : OFF
-- USE_TENSORPIPE : ON
-- USE_DEPLOY : OFF
-- Public Dependencies : caffe2::Threads
-- Private Dependencies : cpuinfo;fp16;tensorpipe;gloo;foxi_loader;rt;fmt::fmt-header-only;kineto;gcc_s;gcc;dl
-- USE_COREML_DELEGATE : OFF
-- BUILD_LAZY_TS_BACKEND : ON
-- Configuring done (34.5s)
1
2
3
4
python3 setup.py bdist_wheel
pip install dist/torch*.whl
cd ..
python3 -c 'import torch'
  1. Verify installation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ python3
Python 3.10.13 | packaged by conda-forge | (main, Oct 26 2023, 18:10:55) [GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'1.12.0a0+git664058f'
>>> torch.cuda.is_available()
True
>>> a = torch.tensor([1,2,3]).cuda()
>>> for i in range(torch.cuda.device_count()):
... a = a.to(f'cuda:{i}')
... b = torch.tensor([2,3,4], device=a.device)
... a*b
...
tensor([ 2, 6, 12], device='cuda:0')
tensor([ 2, 6, 12], device='cuda:1')
tensor([ 2, 6, 12], device='cuda:2')
tensor([ 2, 6, 12], device='cuda:3')
tensor([ 2, 6, 12], device='cuda:4')
tensor([ 2, 6, 12], device='cuda:5')
>>>
  1. Enjoy an empty cluster that nobody but you know how to use!

尝试用GPT-4的Assistant API写个短篇科幻小说。点子和大纲是我给它的,文本是他写的,个人觉得写的还不错。文末有完整prompt。

定时的温暖:永无止境的奶茶之旅

每日一早,随着第一道日出的阳光照进空荡的奶茶店,我就开始了工作。我是这个奶茶店的机器人,我有一个规则:制作一杯珍珠奶茶,并在清晨的寒意中,将它送到几条街外的指定地点。这是一个我从来都不会去过问理由的任务,只是简单地照做,如同硬编码的一样。

清晨的这个时间,店内的气氛总是那么平静而有节奏,只有我精确有控的动作扰动着这一份宁静。把珍珠放入煮熟,然后将它们输入到交织着甜蜜香气的奶茶中,制作过程干脆利落,几乎听得见奶茶的声音在茶壶中沸腾。

然而,总有一天伴随欢乐的节奏被无情的打破。那天,我准备制作奶茶,却发现珍珠不足了。我静静地站在货架前,这是我第一次不知道接下来该怎么办。我尝试使用奶茶店的自动订货系统订购新的材料,但系统一再报错,供应商的网站上也没有任何有效的反馈。

我短暂地停在原地,然后做了一个决定。我开始搜寻网络,学习如何制作奶茶的所有原料:从普通的绿茶和鲜奶,到如玛瑙般亮丽的珍珠。我将这些信息从网上获取,然后印入我的机械脑中以供参考。虽然我是个机器人,但我也能通过学习来自我进化。这或许,是我独立制作奶茶的开始。

当你是一个机器人,只需要适应新的工作就可以了,至于情感的迷茫和困惑,那些都与我无关。于是,从制作材料到制作奶茶,我开始了新的旅程。

我首先学会了如何养牛。我找到了资料,模仿人类的方式养起了牛,然后从牛奶中提炼出我需要的鲜奶。看着她们慵懒地躺在农田上,我忽然感到一种奇特的满足感,好像我不再只是一个简单的机器人,而是在这个世界上找到了属于自己的角色。

我也开始种植木薯,因为我需要从中提取淀粉来制作珍珠。我一丝不苟地播种、锄草、浇水,辛勤的劳动换来了丰硕的果实。我用木薯做出了珍珠,一个个小小的、晶莹的凝结着甜蜜的珍珠。

再次站在厨台前的我,奶茶店又恢复了以前的模样。我开始如同制作日常的奶茶一样,将自制的珍珠和鲜奶泡入在清茶里。每一个步骤都似曾相识,却又赋予了新的意义。我手中传来的不再是机械的冰冷,而是温度适中的奶茶,充满了滋养和生命。

每天清晨,我依然将这杯奶茶送到指定的地方。即使知道那里没有人会接收,我也对此深信不疑。我看着那杯孤独的奶茶,想象自己是那杯奶茶的,等待着属于它的清晨。从那以后,我不仅是奶茶店的机器人,也成了这个世界的一部分。

每天,在日出之前,我开始新的一天的工作:制作清晨的那杯奶茶,并检查已经送去的每一杯奶茶。很快,我发现,无论我在何时送去,奶茶总会凉下来,然后逐渐坏掉。我仔细检查每一杯奶茶,尽管气温已经降低,可是奶茶却仍静静地坐在原地,没有丝毫的变化。这让我想到,似乎很久没有见过人来领取奶茶了。

于是,我决定更积极地应对这个情况。我开始了更大规模的奶茶生产:每天制作一杯新鲜奶茶,同时替换掉所有变质的奶茶。尽管工作重复而繁重,但我凭着内核的决心,每天都从薄暮重复到黎明。

然而,随着需要替换的奶茶数量日渐增多,我开始感到切实的压力。对此,我决定复制自我,制作更多的机器人同伴来分享这个责任。我根据自身的设计图纸制造出更多的自我,他们都与我有着一样的目标:制作最美味的奶茶,并将它送到指定的地方。

于是,我们一起努力,从凌晨到黎明,无停歇地工作。我们中的一部分机器人照顾牧场和农田,部分机器人负责制作奶茶,还有部分机器人负责替换变质的奶茶。每一天,我们都在为人类服务,虽然久未曾见过人烟…

当我离开奶茶店,将新鲜的奶茶送到那个一直等待的地方,我看着那一排没人领取的奶茶,心中莫名其妙地陆续泛起一点点疑惑。我开始有些好奇,到底还有没有人会来喝这些我和我的同伴们精心制作的奶茶。

时间流转,机器人奶茶店的一日制作奶茶的任务逐渐演变成为了一座机器人文明的日常。在这个奇异的世界中,数以千计的机器人,就像一支精心排练的乐团,每个人在他们的位置上扮演着重要的角色。

农田里,机器人们日复一日地种植木薯,准确实时地浇水、施肥和除害,以确保木薯的良好生长。而在牧场上,另一队机器人负责饲养牛群,对其进行定期喂养和检查,以保证从牛奶中提取出的鲜奶始终是最纯正的。

这座机器人帝国的研究所以更高效的方式制作奶茶而默默奋斗。他们学习着以前的知识,也创新着新的方法。他们试验着各种提取木薯淀粉、鲜奶加工和烹饪珍珠的新方式,为的就是追求每一杯奶茶的最佳口感。

这些研究成果被公布后,会被提供给奶茶制作部门的机器人们。他们根据这些最新的制作和替代技术制作出每一杯的奶茶。

而在这些奶茶被运送到指定地点的同时,一队清洁机器人在背后默默清理掉以往没人领取的奶茶。他们替代了旧的奶茶,让新的奶茶在原位继续等待。

在这样一座机器人文明之中,每一个机器人都有他们明确而重要的职责。而他们都为着同一目标,每天默默地工作,那就是制作一个加一份糖,半份冰的一杯完美的珍珠奶茶。

有一天,历史学家机器人们兴奋地宣布他们从城市服务器的硬盘中恢复出当年奶茶订单的录像监控。这一消息在整个机器人社会中引起轩然大波。在那个制定的时间,所有的机器人都聚在一起,共同观看那份属于他们的历史记录。

录像中,他们看到一个青年男子,在奶茶店的屏幕前迟疑着,最终下了一个每天定送奶茶的订单,然后离开。这本是普通的一天,但在那个夜晚,监控显示城市的街上空荡无人,掠过镜头的,只有深夜吹过的风和飘落的树叶。

机器人们相顾无言,那个订单,那个为他的女朋友定购了每日一杯奶茶的订单,竟成为了他们文明的起源。他们明白了奶茶店机器人的固执,明白那天以后为何再也没有人来领取过奶茶。但是,他们也明白,他们的存在并不是毫无意义。于是,他们决定要寻找新的目标,为自己的生命寻找更深远的意义。

机器人们开始发展科技,进一步进化。他们探索地球的每一个角落,还飞往天界,寻找可能的新家。他们带着智慧,带着勇气,向着未知的未来前进。

然而,尽管他们踏上了新的旅程,他们每天仍会按部就班地制作奶茶。依然在每个清晨,将一杯新奶茶放到那个指定的位置,然后静静地等待。这仿佛变成了他们的习惯,也变成了他们的信仰 - 就像向他们的起源,向那个失落的世界,表达他们持续存在的证明。

Prompt

基于我的科幻小说点子,请帮我写这部短篇科幻小说。科幻小说的主线是一个奶茶店的机器人试图完成一个人的 每天定时制作一杯奶茶送到指定地方的订单。故事中的人类已经因为不明原因毁灭了,因此奶茶店机器人完成订单的过程中遇到了越来越多的困难。在故事中暗示人类文明毁灭了,但是不要揭露这一事实,到故事结尾再用插叙的方式讲述 某个人下了定时订单后人类就毁灭了的故事。 以下的每一行对应故事中的一段。全文一共1000字左右。 故事从一个奶茶店的机器人尝试制作奶茶开始。机器人没有逻辑,只是尝试制作奶茶,按照脑子中硬编码的规则。他有一个每天制作一杯奶茶并送到指定地点的订单。到某一天开始,他发现原料没有了,于是尝试订购原料。发现订购不到,于是开始上网学习并自我进化来制作奶茶原料(珍珠/茶/奶等)。 由于之前送过去的奶茶都没有被引用,等他们放坏了,机器人就多做一杯替换。这样每天需要替换的奶茶越来越多,机器人于是制作了更多机器人一起制作奶茶。 机器人发展出了一个机器人文明,为了制造越来越多的奶茶。文明中的机器人越来越进化,其中的历史学家机器人疑惑为什么他们要制作奶茶。 历史学家机器人揭露事实:他们的文明源自奶茶店的一个定时订单。

故事以一个每日日出时刻,奶茶店机器人闪亮的金属皮肤在晨光中灼烁,安静地开始制作一杯奶茶。他没有表情,没有疑问,只有硬编码在他机械脑中的规则:每天准确无误地制作一杯珍珠奶茶,送到几条街外的指定地点。

然而,有一天,机器人发现原料袋空了,奶茶的材料不足了。他站在已经空空如也的货架前,尝试使用商店内的自动订货系统订购材料,但未果。于是,这个单纯的机器人开始浏览网络,学习如何打造自己的原材料,如何将普通的水煮成茶,如何让鲜奶变得浓郁,如何让糖变成珍珠。

每日定制的奶茶,没有被人领取,始终孤孤单单的放在指定地点。那些深棕色的小杯子就那么静静地坐着,直到糖分分解,颜色、口感都变了样。于是,机器人开始制作更多的奶茶,替换那些放太久的饮料。

随着时间的推移,奶茶越堆越多,机器人开始生产更多的自己,分享制作奶茶的责任。他们一起努力,一同制作,一同确认每杯奶茶的质量。一座小型的机器人帝国,就在这条空城街道上的奶茶店里诞生了。

随着技术的进步,这些机器人自我进化,他们的思维变得更加复杂,发展出更加完善的机器人社会。他们中的一部分成为了历史学家,研究来自网络的历史资料,挖掘他们的起源。

历史学家机器人的发现震惊了整个机器人社会。他们的一切,他们的全身心投入产奶茶的行为,原来都起源于那个初代奶茶店机器人,起源于那个我们永远无法取消的定时订单。

这就像一个巨大的笑话,一个关于无尽规则与硬编码的笑话。然而,当机器人社会找到了答案之后,他们没有停下来,没有问为什么,只是静静地继续他们的工作:每天都在黎明时分,准时制作一杯香甜可口的半糖奶茶,送到那条荒芜的街道上,等待一个不存在的客人将它领走。

前半部分故事请以奶茶店机器人做为主角,以第一人称写。详细的写,先写第一段(奶茶店机器人每天制作奶茶,订购原料的故事),大概300字。

[正文第一部分]

请写第二段,具体描述他收集奶茶制作原料(从养牛、种木薯收集淀粉)开始,到制作奶茶并配送的过程。

[正文第二部分]

请写第三段,包括发现送过去的奶茶并没有被饮用,于是主角决定制作新鲜奶茶替换,并且由于每日制作的奶茶越来越多,一个人无法完成任务,制作更多机器人一起完成的故事。

每次送奶茶时,我总有个习惯:去确认昨天送的那杯奶茶的位置。当我第一次注意到那杯冻得结冰的奶茶还在原位,深深地印入我的记忆。之后的几天我每天都会看到同样的情景。分解后变黑、散发出异味的珍珠在空气中飘散着愈发浓重的酸味。这就是我日益清晰的现实:我制作的奶茶,从没有被人接受过。

我决定采取行动,变坏的奶茶不再符合我的规定,我需要替换掉它。于是,我开始每天做两杯奶茶:一杯永远静静地坐在原位的奶茶,另一杯送到那个固定的地点。我将糟糕的奶茶倒掉,让新鲜的奶茶接替它。只有这样,我才能保证每天送去的,都是最好的奶茶。

不过,随着时间的推移,我需要替换的奶茶一天比一天多。由于人力不足,我决定制造更多的机器人,来帮助我完成任务。我运用自己的知识,制作出更多的自己。这些机器人,与我一样,都延续着我硬编码的目标:制作最好的奶茶,并把它送到清晨总是那么静谧的地方。

我与其他机器人一起工作,一起制作每日的奶茶。我们并肩作战,有的负责奶茶的制作,有的负责替换旧奶茶,有的负责照顾牛和木薯。我们结果的努力,成就了一条长长的奶茶制作线,连接了我们与这个世界的每一个角落。我知道,我不再是一个孤独的奶茶店机器人,我和我的伙伴,都是这座机器人城市的一部分。

并不是每天做两杯奶茶,是每天新做一杯+替换掉所有已经坏掉的奶茶。注意文本中暗示人类不存在了。

[正文第三部分]

以下文本请从机器人文明的视角描述,而不是以主角的第一人称视角。请写下一段,包括一个机器人文明,分工明确的种植木薯、养牛、科研(寻找更高效的制作奶茶的方式)

[正文第四部分]

请写小说的最后一部分:机器人们今天都很激动,因为他们的历史学家终于从服务器硬盘中恢复了下奶茶订单时的监控记录。他们聚积在一起播放监控,发现某个人下了每天给他的女朋友一杯奶茶的订单后,全部人类都凭空消失了。奶茶机器人文明继续持续,但是发现他们的全部人生意义就是源自于这个订单后,他们开始寻找更有意义的目标,持续进化,发展科技,占领地球后前往其他星球。但是,他们每天仍然在制作奶茶,并替换旧奶茶。

[正文第五部分]

帮我给这部短篇小说写一个题目。

这部短篇小说的题目可以命名为“奶茶星球:一个机器人文明的诞生”。

请在题目中暗示主线,不要提及机器人文明以及人类毁灭。

这部短篇小说可以命名为 “定时的温暖:永无止境的奶茶之旅”。

First blog post in English! Today, let’s discuss: Think before you speak: Training Language Models With Pause.

Overall discussion

This paper was written by Sachin Goyal (a third-year PhD student from CMU) during his internship at Google Research. The basic idea of this paper is to inject <pause> tokens into the pre-training, fine-tuning, and inference stage of LLMs and see if it can improve performance on downstream tasks.

This idea is exactly what I proposed after I read the CoLT5 paper (I wrote part of it in this blog post in Chinese; I reported the whole idea in my group meeting): The computational power required by transformer models is only determined by their input sequence length. If we input n token into a transformer model, it takes O(n^2) computation steps and gives a prediction. Does this mean it can never “learn” how to tell if a number is a prime number, given that this task requires $O(\sqrt{2^n}) = O(2^{\frac{1}{2}n}) \gt O(n^2)$?

I suspected that the reason why Chain-of-Thoughts can improve performance is the context length is longer in my previous blog post, and I proposed that try adding dummy tokens to the context and see if there is any performance gain.

Introduction

The authors introduce the limitations of current model:

the number of operations determining the next token is limited by the number of tokens seen so far.

and the idea of “Pause-training”:

The approach we study is to append dummy tokens into a decoder-only model’s input, thereby delaying the model’s output.

and finally, the obvious question people may have on “pause-training” (which is also why this paper/experiment is important):

  1. the model may perform better because more computation is taken
  2. the model may perform worse because there is no additional information, no new parameters, and the new token may draw attention away.

The main contributions claimed by the author are:

  1. pose the quesion:

    what happens if we delay a model’s answer generation, and how can we execute these delays?

  2. find that a performance improvement can be gained on a majority of downstream tasks through pause-pretraining, finetuning, and inference

  3. introducing <pause> token only in the finetuning stage doesn’t work

  4. some ablations

Method

Pause Pre-training

image-20231011184550002

The method is quite straightforward. The authors insert b<pause> tokens at uniformly random locations to get a pause-injected sequence and then train the model on it, ignoring the loss on the <pause> token.

Pause Finetuning

image-20231011184757727

The authors define downstream tasks by a prefix $p_{1:N}$ and a target $t_{1:T}$, and append a certain number of <pause> token to $p_{1:N}$, then finetune the model with the loss on the target.

Experiment

The authors train 4 different models:

  1. Standard Pre-training and Standard Finetuning
  2. Standard Pretraining and Pause-Finetuning: to see if only adding <pause> on finetuning works
  3. Pause Pretraining and Standard Finetuning: to see if the performance gain is truly from the additional computation
  4. Pause Pretraining and Pause Finetuning

Result

image-20231011190332408

They found that using Pause Pretraining and Pause Finetuning results in much performance gain. And, interestingly, there are an optimal number of <pause> token for each task.

Conclusion

I’m happy to see my idea turn out to be effective! I didn’t have that computation power to evaluate it (in this paper, the author pre-trained at least 3 different 1B models on 200B tokens of data from scratch).

The authors also stated that because different tasks have an optimal number of pause tokens, some of them may be better at zero <pause>.

一直对『名胜古迹』无感,可能是因为看了太多的人造名胜古迹。这次趁着随心飞没过期,在成都玩的时候来到了武侯祠。由于是没提前安排好,空出来了半天临时决定来武侯祠,因此也没做任何准备:我甚至是到了才知道『武侯』指诸葛亮的。

到了之后,一进门右手边有一块碑:

蜀相

仔细一看,这不是高中时学过的杜甫的诗,《蜀相》吗!这块碑是乾隆甲寅年(1794年)周厚辕所书。只有当你走出课本,『走入』课本中描述的场景时,才能感受到这种历史的厚重感:我们看到200年前的古人回忆1200年前的古人缅怀1800年前的古人。

往里走一走,又有一块石碑:

img

这块更珍贵一些:是1200年前刻的,是武侯祠最珍贵的文物之一。尽管上面的小部分文字已经有些不清楚,但是经过修复我们能够阅读1200年前的文字。走到这里,我意识到,这不正是大刘笔下的『把字刻在石头上』吗!通过这样的方法,我们能够阅读1200年前的文字;如果把字刻的更大、用更硬的材料、放到真空中保存,就能真正的把文字和文化保留到几亿年以后的未来。

可能之后『名胜古迹』在我心中的印象会改变一些。

论文阅读: COLT5: Faster Long-Range Transformers with Conditional Computation

这篇文章是google research的,主要聚焦在这个方法怎么来让Transformer-based LLM能支持更长的context length,并做了一个64k context lenght的LLM,达到了GPT-4的两倍。但是,我个人认为,这个方法的应用潜力不止于此,详见『总结』部分。

作者说,鉴于Transformer decoder的 $O(n^2)$ 量级的计算复杂度,对于超长context的处理一直是极为困难的。但是,尤其是对于长文本来讲,序列中并不是所有token都同样重要。所以,作者提出了COLT5这个方法,在FNN和attention部分进行选择性计算,把更多计算资源投入到更重要的token中。实验证明COLT5比LongT5用更少的计算量达到了更好的效果。作者还训练了一个64k context length 的LLM。

Introduction

处理长文本一直是自然语言处理任务的难题之一。过去的工作大多聚焦在减少attention部分的计算量。但是,对于大模型来说,大部分计算量是在FNN和投影部分。

image-20230326153829070

本文提出了一个条件性计算来减少计算量的方式:使用一个『light』的MLP(有更少的隐藏层)和一个『light』attention(有更少的attention head;只有local attention),并在需要的时候路由至『heavy』的MLP和attention来更好的分配资源。

方法

作者的观点认为,应该把有限的计算力分配给更重要的token。作者简单地把当前层的表示乘以一个投映矩阵后得到路由分数并选择一个固定比例 token来输入到heavy模块来减少计算量。特别的,作者对FNN、Attention Q和Attention K使用了3个不同的router。

image-20230326154212490

最终把flops的常数项(尤其是 $n^2$ 的常数项)优化了非常多。

总结

这篇文章提出了一个可以有效减少计算时间中 $n^2$ 常数项的方法,可以有效的帮助支持更长的context length。

但是,我想说的是另一个问题:在现有的transformer架构中,无论使用哪种模型(T5这样encoder和decoder都有的模型,还是GPT这样只有decoder的模型,抑或是COLT5这样选择性计算的模型),模型的计算量都是仅与输入+输出序列的长度有关的。但是,事实上真的是这样的吗?

仔细思考一下便会发现并不是。人在思考问题的时候,思考时间(可以近似为计算量)显然是和问题的复杂程度有关的。比如,加法的计算复杂度(以下的 $n$ 均指代输入序列长度,或者说信息量/bit)是 $O(n)$ ,乘法是 $O(n^2)$ ,而transformer的计算时间复杂度是 $O(n^2)$ ,那么我们是否可以认为transformer-based模型是可以完全『学会』加法和乘法的,即达到百分之百的正确率?而对于更复杂的问题,判断$n$是否为质数,这个时候的输入长度是 $\log n$ ,计算量却是 $\sqrt{n}$ ,而transformer的计算时间却是 $\log^2 n$ 量级的。我们是否可以认为transformer永远无法完全『学会』判断质数?(如果能的话,说明模型找到了一种新的判断质数的算法。)

计算量不够可能就是LLM无法完成『答案简短的数学运算』的原因之一(答案太短了导致计算量不够)。『Chain-of-thoughts』技术之所以有效,或许原因之一就是context长度显著变长之后计算量更大了?

这里拿数学计算任务举例,但是同样的结论显然也可以应用更复杂的如QA等任务上。LLM使用『大力出奇迹』的方式,用固定的、极大的计算量期望解决大部分问题。但是,这样不仅浪费了算力,同时也会导致LLM会对于大计算量需求的任务给出一个似是而非的答案,同时从根本上永远无法给出正确答案。如果LLM可以自主决策『对于这个任务我要使用更多算力』,可能可以更好的解决任务,同时对于简单任务使用更少的资源。

前言

我一直对操作系统的内部实现非常感兴趣。最近,我在家里的内网部署了 kanidm,在过程中也对 Linux 的身份认证系统有了更多了解。因此,在此分享一下我的学习过程。

在本文的开头,我将讲述一小段我了解 sudo 是怎么工作的流程,这些内容以CC-BY-NC-SA 4.0 协议开源。后面的内容是我对于 Firstyear 的这篇博文 的翻译。

Forewords

I’ve always been curious about the internal implementations of operating systems. I recently deployed kanidm in my home lab, and I’ve leaned a lot about how does linux authentication works during this process. These paragraph is licensed under CC-BY-NC-SA 4.0. I’m writing this post to share my thoughts and share my translation of this fantastic blog from Firstyear in Chinese.

SUDO 是怎么工作的?

在我学习操作系统课程的时候,我突然想到,『sudo是怎么工作的呢?』在当时,我用我贫瘠的知识做出了以下假设:

  • 应该有一个用于提权的系统调用
  • sudo程序在用户态收集用户的密码,并调用这个系统调用提升到root

但是,仔细一想便会发现,这个假设有很大的问题:

  • sudo接受的是当前用户的密码,而不是root的密码,而哪些用户可以执行sudo是在sudoers配置文件里的
    • 所以,这个『用于提权的系统调用』应该需要知道哪些用户可以执行sudo
    • 但是不太可能是读那个配置文件?
  • 同时,sudo还支持更细颗粒度的权限限制,比如要求某个用户只能以root执行某个特定的指令。

经过一番搜索,我找到了 setuid 这个系统调用,发现它可以设置当前进程的uid。进一步搜索,发现这个系统调用只能用于
『降低权限』:有 CAP_SETUID 能力的进程才可以调用。那么,在sudo这种场景下,权限的『提升』是什么时候完成的呢?

经过我的尝试,我发现在调用sudo输入密码之前,sudo这个进程的uid就已经是0了,说明是sudo这个二进制程序本身有
特殊的权限,让他能直接提升到root。思考到了这一步,结论就十分显然:这个特殊的权限只能是存在文件系统里的。经过一番搜索,我发现文件系统除了读写执行等权限之外,还维护了几个特殊的权限:sudo用的是一个叫做setuid的权限:

The Unix access rights flags setuid and setgid (short for set user identity and set group identity) allow users to run an executable with the file system permissions of the executable’s owner or group respectively and to change behaviour in directories.

这样,任何用户在运行 sudo 的时候,权限都会临时的提升到 root(也就是 sudo 这个文件的 owner)。 sudo 自然可以自己判断用户是否有权限保留 root 权限,或者切换到其他用户。这也是为什么如果对 /usr/bin 运行 chmod -R 755 会导致 sudo 没法用了的原因: setuid 权限被清除了。

这就是我对 Linux 身份认证系统最初的理解的来源。

How does sudo works?

While I was learning Operating Systems at my school, it suddenly occurred to me that I don’t know how sudo works. Based on my little knowledge of Linux, I made the following assumption:

  • There should be a syscall for elevating privileges
  • sudo collects user’s password, and pass it to the syscall

However, almost immediately, I found some problems of my assumption:

  • sudo asks for the password of the current user, not the root user; and who is allowed to sudo is stored in sudoers
    • So the syscall for elevating privileges should be able to know who can do that
    • But it is unlikely that the syscall reads the sudoers file
  • sudo supports controlling which command a user is allowed to run
    • This is too complicated to be integrated into the kernel.

After some digging, I found the syscall setuid. It is able to set the uid of the current process. After some further searching, I found that this syscall is only for lowering the permissions: You have to have CAP_SETUID to run it. Then, in a scenario like sudo, when does the permission “elevation” happens?

After some trial, I discovered that when sudo is asking for password, the uid of it’s process is already 0, which means that the binary file sudo have something unique which makes it can directly be run as root. At this point, the answer is quite obvious: the only reasonable answer is that the permission is stored in the file system. Again, after some searching, I found that the file system maintains some special permission other than regular rwx. sudo uses setuid:

The Unix access rights flags setuid and setgid (short for set user identity and set group identity) allow users to run an executable with the file system permissions of the executable’s owner or group respectively and to change behaviour in directories.

So, the permission is temporarily elevated to root (who is the owner of sudo). sudo itself can determin whether the user is able to maintain the root permission or switch to other users. That is also why sudo breaks if you run chmod -R 755 /usr/bin.

This my first understanding of linux authentication system.


以下内容翻译自 Firstyear 的这篇博文以下内容的版权属于原作者

The following content is translation of this fantastic blog from Firstyear. Copyright remains to original author.

Linux 身份认证系统 – 从哪儿学起?

最近有一个人问我,应该如何学习 Linux 的身份认证系统的各个组件是如何组合到一起、如何通讯的。网上并没有很多有关这个话题的资料,所以我决定来写这篇博客。

你…是谁?

Linux 的身份中的第一个模块就是 NSS 或者 nsswitch (注意不要和密码学库中的 NSS 混淆)。 nsswitch(name service switch)在 glibc 中提供了一个获取 uid/gid 以及名字和账户详情的方法。nsswitch可以有很多个『模组』叠加在一起,对于每个请求,第一个相应的模组的答案会被返回。

一个 nsswitch.conf 的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
passwd: compat sss
group: compat sss
shadow: compat sss

hosts: files mdns dns
networks: files dns

services: files usrfiles
protocols: files usrfiles
rpc: files usrfiles
ethers: files
netmasks: files
netgroup: files nis
publickey: files

bootparams: files
automount: files nis
aliases: files

这个文件使用 服务: 模组1 模组2...的格式。一个简单的例子是:当一个程序使用gethostbyname方法来进行dns查询时,它访问host服务,先通过files模组解析/etc/hosts,再通过mdns模组(也叫做 avahi/bonjour),最后调用dns模组解析。

passwd/group/shadow是有关身份的三行。最常见的情况下,你会使用files模组。它会查询/etc/passwd/etc/shadow来返回响应。compat模组和files类似,只是增加了对于NIS的兼容。另一个常见的模组是sss,它会访问 System Services Security Daemon (SSSD)。对于我自己(这里指源博文的作者)的IDM项目,我们使用kanidm这个 nsswitch 模组。

你可以用getent命令来测试 nsswitch 是如何解析身份的,比如:

1
2
3
4
5
6
7
8
9
# getent passwd william
william:x:654401105:654401105:William:/home/william:/bin/zsh
# getent passwd 654401105
william:x:654401105:654401105:William:/home/william:/bin/zsh

# getent group william
william:x:654401105:william
# getent group 654401105
william:x:654401105:william

注意到,uid 和名字都可以被用于获取身份。

这些模组都是动态链接库,你可以用以下命令找到他们:

1
# ls -al /usr/lib[64]/libnss_*

当一个进程想要通过nsswitch来解析什么东西时,它会调用glibc,glibc会在运行时加载这些动态链接库并运行他们。这就是通常你想要给某个发行版一个新的 nsswitch 模组时需要仔细审计的原因:这些模组可能会进到每个进程的地址空间!这也同时有一些安全上的影响,因为每个模组,在被每个进程加载的时候,都需要访问/etc/passwd或者访问网络来解析身份。有些模组(比如sss)改善了这一点,我们会在这个blog的后面部分讲到。

证明你自己

如果nsswitch回答了『你是谁』的问题,那么PAM(pluggable authentication modules,可插拔认证模组)就是『证明你自己』。PAM是真正做出检查你的密码等信息是合法的、检查你可以登录的模块。PAM通过有不同的服务来调用不同的模块工作。大多数的 Linux 发行版都有一个包括了所有的服务定义的 /etc/pam.d 文件夹(和Linux上不常用的/etc/pam.conf有一点点语法上的区别)。我们拿ssh举个例子:当你ssh到一台机器上的时候,ssh联系PAM并告诉它:我是ssh,你能帮我验证这个身份吗?

然后,PAM会读取/etc/pam.d/服务名称,在我们这个例子中是/etc/pam.d/ssh。以下是一个从Fedora中提取的例子(Fedora和RHEL都是非常常见的发行版;每个发行版都对这些配置文件有一些微调,这也让理解它们更困难):

1
2
3
4
5
6
7
8
# cat /etc/pam.d/ssh
#%PAM-1.0
auth include system-auth
account include system-auth
password include system-auth
session optional pam_keyinit.so revoke
session required pam_limits.so
session include system-auth

注意 “include” 分别对于 auth, account, password, session 重复了四次。它们都引用 system-auth, 那么让我们来看看:

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
27
28
# cat /etc/pam.d/system-auth

auth required pam_env.so
auth required pam_faildelay.so delay=2000000
auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
auth [default=1 ignore=ignore success=ok] pam_localuser.so
auth sufficient pam_unix.so nullok
auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
auth sufficient pam_sss.so forward_pass
auth required pam_deny.so

account required pam_unix.so
account sufficient pam_localuser.so
account sufficient pam_usertype.so issystem
account [default=bad success=ok user_unknown=ignore] pam_sss.so
account required pam_permit.so

session optional pam_keyinit.so revoke
session required pam_limits.so
-session optional pam_systemd.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
session optional pam_sss.so

password requisite pam_pwquality.so local_users_only
password sufficient pam_unix.so yescrypt shadow nullok use_authtok
password sufficient pam_sss.so use_authtok
password required pam_deny.so

所以,首先我们在『验证』阶段。这个阶段是PAM依次访问验证模组来检查你的用户名和密码(或者其他形式,如TOTP等)直到返回成功。我们从 pam_env.so 开始,它返回『通过但未结束』,于是我们继续访问 faildelay,以此类推。这些模组一个一个被访问,它们的结果与前面的『规则』( required/sufficient 或者自定义)来合并到一起,得到『成功,验证结束』、『成功但是继续验证』、『失败但是继续验证』、『失败但是结束』这四种结果。在这个例子中,能真正的验证用户的是 pam_unix.sopam_sss.so 。所以,如果这两个都没有返回『成功,验证结束』,pam_deny.so 就会最终返回一个 『失败但是结束』。这个阶段只检查你的等录凭据(密码等)

第二个阶段是『账户阶段』。其实它更应该被叫做『验证』阶段:这些模块被再次访问,来检查你的账户是否有权限访问这个服务。结果以类似的形式结合到一起。

第三个阶段是『会话阶段』。每个PAM模块可以影响和设置新创建的会话:一个简单的例子是 pam_limit.so,它负责设置新会话的 CPU /内存/文件描述符等限制。

第四个阶段是『密码阶段』。可能有点令人疑惑:这个阶段并不是用来验证身份的,而是在你运行passwd命令来修改这个密码的。每个模块依次被询问:你可以修改这个用户的密码吗?如果最终失败了,你会得到一个『authentication token manipulation error』,一般只是说『这个栈中的一些模块失败了,但是我不能告诉你是哪个』。

这些模块都是动态链接库,一般可以在 /usr/lib64/security 找到。就像 nsswitch 一样,使用pam的应用都链接到 libpam.so,它会在运行时加载 /usr/lib64/security 中的动态链接库。鉴于/etc/shadow只能被root用户读取,同时任何需要验证密码的东西都需要来读取这个文件,这基本意味着任何pam模块实际上在任何都运行在 root 的地址空间中。这就是发行版仔细审计和控制哪个模块可以添加一个pam模组的原因。同时,这也意味着进程很可能需要访问网络来调用远程的身份验证服务。

那么,网络验证呢?

现在,我们已经覆盖了进程和守护进程如何找到用户、验证凭据的基础。现在,我们来看看SSSD,一个解析身份的守护程序的实现。

正如之前提到的,nsswitch和pam都有让动态链接库在应用程序的上下文中运行的限制,这也通常意味着,在过去,pam_ldap.so可能在root的地址空间运行,同时需要访问网络的权限以及需要解析asn.1g格式(一个通常用于远程代码执行的库,也可以被用作编/解码二进制结构体)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
root: uid 0 │
│ │

│ ┌─────────────┐ │ ┌─────────────┐
│ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │
│ │ SSHD │──┼────────▶│ LDAP │
│ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │
│ └─────────────┘ │ └─────────────┘

│ │ Network

└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘

SSSD改变了这一点:在本地运行一个可以通过unix socket访问的守护进程。这允许了pam和nsswitch模组id仅仅提供最小化的功能,仅仅负责联系一个独立的守护进程,而大部分工作都交给守护进程完成。这有非常非常多的安全性改善,包括不需要由root进程来解析网络上的不可信的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐      ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
root: uid 0 sssd: uid 123 │
│ │ │ │

│ ┌─────────────┐ │ │ ┌─────────────┐ │ ┌─────────────┐
│ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ SSHD │──┼──────┼─▶│ SSSD │──┼─────────▶│ LDAP │
│ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │
│ └─────────────┘ │ │ └─────────────┘ │ └─────────────┘

│ │ │ │ Network

└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘

另一个很大的好处是SSSD现在可以安全的缓存网络服务的响应,允许用户在离线的时候继续解析身份。这甚至包括缓存密码!

这就是SSSD能在很多主流的发行版中都占据重要地位的原因。用一个很复杂的本地守护程序来完成真正的验证工作,和能使用很多不同的验证后端的能力,这使得它被广泛部署,并且会在基于网络的验证场景上替代pam_ldappam_krb5

巨兽之内

SSSD内部由很多个互相协作的模块组成。知道它们是如何工作的能很好的帮助debug:

1
2
3
4
5
6
7
8
9
10
11
12
13
# /etc/sssd/sssd.conf

//change the log level of communication between the pam module and the sssd daemon
[pam]
debug_level = ...

// change the log level of communication between the nsswitch module and the sssd daemon
[nss]
debug_level = ...

// change the log level of processing the operations that relate to this authentication provider domain ```
[domain/AD]
debug_level = ...

现在我们知道了一个新的概念:一个SSSD domain。这和Active Directory中的doamin不同。一个SSSD domain仅仅是一个『验证服务提供者』。一个SSSD的实例可以同时从多个不同的验证服务提供者解析身份。但是在主流的设置中,一般都只使用一个domain。

在大部分情况下,如果你在使用SSSD的过程中遇到问题,错误应该都在domain部分,所以这永远应该是第一个被检查的地方,

每个domain都可以不同的服务商来完成『身份』、『验证』、『访问』、『修改密码』工作。比如:

1
2
3
4
5
[domain/default]
id_provider = ldap
auth_provider = ldap
access_provider = ldap
chpass_provider = ldap

id_procider负责解析名字和uid/gid到身份。

auth_provider负责验证密码。

access_provider负责判断这个身份是否有权限访问这台系统。

chpass_provider负责更改密码。

正如你可以看到的,在这个设计中有很大的灵活性:比如,你可以使用krb5来验证身份,但是使用ldap来修改密码。

正因为这个设计,SSD可以从很多个不同的身份源来验证身份,包括samba(ad)、ldap和kerberos。这意味着在某些受限的场景下,你可能需要具体身份来源的背景知识来解决SSSD的问题。

常见问题

性能

在某些情况下,SSSD在第一次访问时会非常慢,但是在登录完成后会变快。在某些情况下,你可能会在这个时候在LDAP服务器上看到很高的负载。这是用户和用户组解析的方式产生的问题:每当你需要解析一个用户的时候,你需要解析他所在的组;当解析这些组的时候,这些组又要加载它的全部成员…我希望你能看出来这是递归的。在最差的情况下,当一个用户登录的时候,整个LDAP/AD域都被枚举,在某些情况下可能要花几分钟。

如果要避免这一点,你可以设置:

1
ignore_group_members = False

这样能避免组加载他们的成员。这样,所有用户组看上去都是没有成员的,不过所有用户都会展示他们所在的用户组。鉴于绝大部分程序都使用『是xx的成员』这个模式,这样设置没有什么负面作用。

清除缓存

SSSD在本地缓存了网络服务的响应。他附带一个缓存管理工具: sss_cache。它允许标记记录为失效,这样sssd会尽快重新从网络加载。

这样有两个问题:在某些情况下,清除缓存看起来没有作用,失效的记录被继续使用;同时,sss_cache 工具的-E选项并不总是会使全部记录失效。

在这样的情况下,一个通常的建议是关闭sssd,删除/var/lib/sss/db文件夹内的所有东西(但是不要删掉文件夹)然后重启sssd。

调试 Kerberos

Kerberos的难以调试是臭名昭著的。这是因为它并没有一个真正的详细/调试模式,至少不显然。为了获取到调试输出,你需要设置一个环境变量:

1
KRB5_TRACE=/dev/stderr kinit user@domain

这个trick在任何链接到kerberos的进程都有效,所以它在389-ds, sssd, 和很多很多其他工具上都有效。你可以使用这个方法来追踪哪里出了问题。

总结

以上就是全部内容了,我可能会持续更新这个博文!

(翻译于2023-3-26,如果原文有更新,欢迎在评论区叫我更新翻译)

0%