九维我操你爹
这个问题的迷人之处在于,甚至 StackOverflow 都无法给出正确的回答:
https://stackoverflow.com/questions/47968861/does-python-logging-support-multiprocessing: 高赞回答全错
https://stackoverflow.com/questions/1154446/is-file-append-atomic-in-unix: 高赞回答全错
其中有个回答非常具有迷惑性,这个博客 (https://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) 里用 bash 做了 O_APPEND 实验,方法是 20 个进程并行 echo "$line" >> /tmp/out.tmp ,由于 echo 默认会输出 \n,最后验证一下 /tmp/out.tmp 里是否有预期的行数和字节数(数据完整性)、每一行的字节数是否为预期值(单次 write 的原子性),最后发现在 Linux ext3 上 4096 是分水岭,4097 时会出现碎片,进程之间的 write 会穿插输出。我在 Linux 6.17 ext4 上可以复现。
上面这个实验得到的错误结论毒害了整个 SO 很多年,大量的回答都在复读 PIPE_BUF (4096) 这个数,然而 ta 的实验错在 bash echo 的行为很隐晦,如果 echo 的数据(含 \n suffix)大于 4096 并且输出 fd 是 regular file,echo 会拆成多个 buffer 调用多次 write,根本就没有测试到 write 的原子性。
真相是 O_APPEND 是具有多进程原子性的,在 https://elixir.bootlin.com/linux/v6.18.29/source/mm/filemap.c#L4406 的 generic_file_write_iter 里:
generic_write_checks() 会
此外,就算没有 O_APPEND,现代 Linux 也实现了相当程度的 write 原子性,在 man 2 write 里,最后一节 BUGS:
就是在说,如果两个进程的 fd 指向同一个 file description(如图),那它们共享同一个 file offset,自动拥有原子性和互斥性。
内核实现是在 https://elixir.bootlin.com/linux/v6.18.29/source/fs/file.c#L1200 的 file_needs_f_pos_lock 里,如果引用计数大于一,有多进程共享,会 mutex_lock(&file->f_pos_lock) 上锁
其中 FMODE_ATOMIC_POS 对于 regular file 是自动加上的,注释标明这是 SUSv4 的要求
不少著名服务其实依赖了这个行为,比如 nginx 的 worker log 都是从 master fork 继承过来的,那无需 O_APPEND 就能正确运行;Python 著名的 WSGI server gunicorn 也是这种 fork 继承 log fd 模式,曾经有人提问它怎么保证日志输出不会产生多进程 race,我也曾百思不得其解: https://github.com/benoitc/gunicorn/issues/1272
这是我九年前在北京日夜学习思考记录在 trello 的最后一个未解之谜,虽然多少有点焦虑 LLM 的强大,依然很高兴自己能在 AI 辅助下以前所未有地速度解决复杂问题。(这样就有更多时间摸鱼和玩游戏了😉)
(@refault_any 考古 2002 年 Linus 撕逼提到 O_APPEND 也很有趣: https://lore.kernel.org/all/Pine.LNX.4.33.0208011613440.1315-100000@penguin.transmeta.com/
https://stackoverflow.com/questions/47968861/does-python-logging-support-multiprocessing: 高赞回答全错
https://stackoverflow.com/questions/1154446/is-file-append-atomic-in-unix: 高赞回答全错
其中有个回答非常具有迷惑性,这个博客 (https://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) 里用 bash 做了 O_APPEND 实验,方法是 20 个进程并行 echo "$line" >> /tmp/out.tmp ,由于 echo 默认会输出 \n,最后验证一下 /tmp/out.tmp 里是否有预期的行数和字节数(数据完整性)、每一行的字节数是否为预期值(单次 write 的原子性),最后发现在 Linux ext3 上 4096 是分水岭,4097 时会出现碎片,进程之间的 write 会穿插输出。我在 Linux 6.17 ext4 上可以复现。
上面这个实验得到的错误结论毒害了整个 SO 很多年,大量的回答都在复读 PIPE_BUF (4096) 这个数,然而 ta 的实验错在 bash echo 的行为很隐晦,如果 echo 的数据(含 \n suffix)大于 4096 并且输出 fd 是 regular file,echo 会拆成多个 buffer 调用多次 write,根本就没有测试到 write 的原子性。
# $ strace -e write -fTtt bash -c 'line=$(printf "%4096s" "" | tr " " A); echo "$line" >> /tmp/a'
16:36:20.792871 write(1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 4096) = 4096 <0.000043>
16:36:20.792936 write(1, "\n", 1) = 1 <0.000007>真相是 O_APPEND 是具有多进程原子性的,在 https://elixir.bootlin.com/linux/v6.18.29/source/mm/filemap.c#L4406 的 generic_file_write_iter 里:
ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
[...]
inode_lock(inode);
ret = generic_write_checks(iocb, from);
if (ret > 0)
ret = __generic_file_write_iter(iocb, from);
inode_unlock(inode);
[...]
}generic_write_checks() 会
if (iocb->ki_flags & IOCB_APPEND) iocb->ki_pos = ... 来推进 pos 指针,然后 __generic_file_write_iter() 写入数据,这两步都在 inode_lock(inode) semaphore 保护下,多进程安全。此外,就算没有 O_APPEND,现代 Linux 也实现了相当程度的 write 原子性,在 man 2 write 里,最后一节 BUGS:
BUGS
According to POSIX.1-2008/SUSv4 Section XSI 2.9.7 ("Thread Interactions with Regular File Operations"):
All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2008 when they operate on regular files or symbolic links: ...
Among the APIs subsequently listed are write() and writev(2). And among the effects that should be atomic across threads (and processes) are updates of the file offset. However, before Linux 3.14,
this was not the case: if two processes that share an open file description (see open(2)) perform a write() (or writev(2)) at the same time, then the I/O operations were not atomic with respect to up‐
dating the file offset, with the result that the blocks of data output by the two processes might (incorrectly) overlap. This problem was fixed in Linux 3.14.
就是在说,如果两个进程的 fd 指向同一个 file description(如图),那它们共享同一个 file offset,自动拥有原子性和互斥性。
内核实现是在 https://elixir.bootlin.com/linux/v6.18.29/source/fs/file.c#L1200 的 file_needs_f_pos_lock 里,如果引用计数大于一,有多进程共享,会 mutex_lock(&file->f_pos_lock) 上锁
static inline bool file_needs_f_pos_lock(struct file *file)
{
if (!(file->f_mode & FMODE_ATOMIC_POS))
return false;
if (__file_ref_read_raw(&file->f_ref) != FILE_REF_ONEREF)
return true;
[...]
}其中 FMODE_ATOMIC_POS 对于 regular file 是自动加上的,注释标明这是 SUSv4 的要求
/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */
if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))
f->f_mode |= FMODE_ATOMIC_POS;不少著名服务其实依赖了这个行为,比如 nginx 的 worker log 都是从 master fork 继承过来的,那无需 O_APPEND 就能正确运行;Python 著名的 WSGI server gunicorn 也是这种 fork 继承 log fd 模式,曾经有人提问它怎么保证日志输出不会产生多进程 race,我也曾百思不得其解: https://github.com/benoitc/gunicorn/issues/1272
这是我九年前在北京日夜学习思考记录在 trello 的最后一个未解之谜,虽然多少有点焦虑 LLM 的强大,依然很高兴自己能在 AI 辅助下以前所未有地速度解决复杂问题。(这样就有更多时间摸鱼和玩游戏了😉)
(@refault_any 考古 2002 年 Linus 撕逼提到 O_APPEND 也很有趣: https://lore.kernel.org/all/Pine.LNX.4.33.0208011613440.1315-100000@penguin.transmeta.com/
找在北京的朋友问了下他出差的地方附近有哪些好吃的,希望能在最后几天帮北京改善下印象。结果老同学已经绝望到把最后的期待留给冰糖葫芦了……
我说夏天不兴吃这个,同学问我那夏天吃什么?
是啊,吃什么?一时除了柳絮真的想不到其他答案了(
however the benchmark were like running forever
我们先从花见的现状和历史聊起,走进了樱花那个美丽又复杂的象征世界。花见给日本带来了多少经济效益,又带来了多少麻烦?花见历史是怎么发展的?日本人为什么这么喜欢樱花?之后聊到樱花这个植物本身,再到食用樱花,进入香豆素的兔子洞(小剧透:从樱花居然可以聊到我们熟悉的肉桂)
最后我们来到樱桃。北美超市最常见的樱桃,背后居然藏着一段华人移民的伤痕故事?还有——小时候生日蛋糕上那颗红得夸张、味道奇怪的樱桃,背后到底是什么来头?以及,樱桃为什么在西方文化是「性感」的化身?
同一个「樱」字,可以是好看的花朵,可以是好吃的水果,可以是物哀之美,可以是宣传工具,可以是欲望投射,来和我们一起探索吧!
收听链接:
小宇宙 | Pocketcasts | Spotify | Apple
#podcast #serendipedia维机游走
我这边下午针对 nonthinking / reasoning tasks 补充了一些测试,到现在还没跑完(目前出来的结果可以看截图)。说实话跑到这里我已经对 qwopus 没什么信心了,既然网友不想测了我干脆也放弃这个模型了
明天测下 qwen 3.6 原版的智商,不出意外之后 local LLM 就跑它了