diff --git a/2019/12/30/summary-2019/index.html b/2019/12/30/summary-2019/index.html index acc33c1..648c42a 100644 --- a/2019/12/30/summary-2019/index.html +++ b/2019/12/30/summary-2019/index.html @@ -7,7 +7,7 @@ - + @@ -166,7 +166,7 @@ isHome: false, isHighlightShrink: false, isToc: false, - postUpdate: '2025-01-07 10:39:22' + postUpdate: '2025-03-17 21:25:21' }

年终总结 - 2019

  还有24h,2019就结束了
  本来想说,今年是过去17年中最糟糕的一年
  但仔细想想,大概之后的每一年都会是这样,没什么意思,所以也不想写很多情绪化的东西,就随便说说好了。
  
  我想,这一年我学到的唯一的道理,就是
  “在学校之外的很多事情,自有他们发展的规律。有很多事情,努力除了能让自己不后悔以外,什么也改变不了。”
  
  今年第一次真正认识到人的渺小,在意外、灾难、疾病面前,人的力量是薄弱的,甚至,连一点挣扎的机会都没有。在无锡侧翻的高架桥下的人也许永远也料不到自己会被180吨的轧钢要去了性命,重庆广场305楼下的两名学生也绝不会想到自己会因为一具没了灵魂的躯壳而永远停留在17岁。就像任何人都无法躲避死亡,我们所有的观念,行为无非是在改变着我们走向终点的路途罢了。
  
  我自己的经历也不例外。我以为自己可以做到,我以为只要我倾注足够的精力,我祈祷,我等待,事情就可以有所改变,甚至还凭着侥幸心理还在知乎上写了一篇回答,给别人讲方法,提帮助。
  就像在用沙子搭积木,一粒一粒地把沙子垒起来,注水,看它们黏在一起,即便我知道当水全部蒸发的时候,所有的努力都会化为泡影,但我不信这个邪,我骗自己,只要我不断地修补、维护,水就永远不会蒸干,于是一眨眼便是三年。
  但突然有一天,一阵风吹过,带走了不起眼角落里最后一滴水,什么也没有了。
  
  曾经看过一个理论,在高维的空间里,时间像长、宽、深那样是既定的,也就是说我们现在经历的,过去发生的,和即将发生的一切,都是存在的。
  我想了很久,难道这不就意味着混吃等死吗?
  也还是有一些差别的。对于处于三维的我们,未来之所以是未来,正是因为它的不可预见性,就像看不到的立方体的背面那样。即使我能猜到它背面的样子,即使翻转它不会对它有一分一毫的改变,但我仍然对它抱有好奇,好奇和我想象的有什么差别。
  努力也许改变不了结果,但努力的过程会创造回忆。
  站在2019的终点看之前的365个日夜,有开心,有难过,有绝望。我看到自己因为一句话而高兴,又因为一些事而失落;我看到自己疯癫的样子,也看到自己沉默的样子;我看到一些人因为我而高兴,也看到一些人被我波动的情绪所影响…所有的这一切构筑了我来到这世界第17年的回忆,我的2019年。
  “如果我什么也改变不了,那我就在这个世界里创造一些回忆吧。”
  (正文完)
  
  P.S.过去的一年因为自己的原因情绪波动很大,很多时候都因为失控没有去注意正常的情绪表达,多多少少都对身边的人产生了影响。借此机会,我希望向很多人道歉,真的对不起。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2019/12/30/summary-2019/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

年终总结 - 2019

  还有24h,2019就结束了
  本来想说,今年是过去17年中最糟糕的一年
  但仔细想想,大概之后的每一年都会是这样,没什么意思,所以也不想写很多情绪化的东西,就随便说说好了。
  
  我想,这一年我学到的唯一的道理,就是
  “在学校之外的很多事情,自有他们发展的规律。有很多事情,努力除了能让自己不后悔以外,什么也改变不了。”
  
  今年第一次真正认识到人的渺小,在意外、灾难、疾病面前,人的力量是薄弱的,甚至,连一点挣扎的机会都没有。在无锡侧翻的高架桥下的人也许永远也料不到自己会被180吨的轧钢要去了性命,重庆广场305楼下的两名学生也绝不会想到自己会因为一具没了灵魂的躯壳而永远停留在17岁。就像任何人都无法躲避死亡,我们所有的观念,行为无非是在改变着我们走向终点的路途罢了。
  
  我自己的经历也不例外。我以为自己可以做到,我以为只要我倾注足够的精力,我祈祷,我等待,事情就可以有所改变,甚至还凭着侥幸心理还在知乎上写了一篇回答,给别人讲方法,提帮助。
  就像在用沙子搭积木,一粒一粒地把沙子垒起来,注水,看它们黏在一起,即便我知道当水全部蒸发的时候,所有的努力都会化为泡影,但我不信这个邪,我骗自己,只要我不断地修补、维护,水就永远不会蒸干,于是一眨眼便是三年。
  但突然有一天,一阵风吹过,带走了不起眼角落里最后一滴水,什么也没有了。
  
  曾经看过一个理论,在高维的空间里,时间像长、宽、深那样是既定的,也就是说我们现在经历的,过去发生的,和即将发生的一切,都是存在的。
  我想了很久,难道这不就意味着混吃等死吗?
  也还是有一些差别的。对于处于三维的我们,未来之所以是未来,正是因为它的不可预见性,就像看不到的立方体的背面那样。即使我能猜到它背面的样子,即使翻转它不会对它有一分一毫的改变,但我仍然对它抱有好奇,好奇和我想象的有什么差别。
  努力也许改变不了结果,但努力的过程会创造回忆。
  站在2019的终点看之前的365个日夜,有开心,有难过,有绝望。我看到自己因为一句话而高兴,又因为一些事而失落;我看到自己疯癫的样子,也看到自己沉默的样子;我看到一些人因为我而高兴,也看到一些人被我波动的情绪所影响…所有的这一切构筑了我来到这世界第17年的回忆,我的2019年。
  “如果我什么也改变不了,那我就在这个世界里创造一些回忆吧。”
  (正文完)
  
  P.S.过去的一年因为自己的原因情绪波动很大,很多时候都因为失控没有去注意正常的情绪表达,多多少少都对身边的人产生了影响。借此机会,我希望向很多人道歉,真的对不起。

+
文章作者: Linloir
文章链接: https://blog.linloir.cn/2019/12/30/summary-2019/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

年终总结 - 2020

准切地说,这篇年终总结来得有些晚了,晚了一个月十一天二十三小时五十九分钟。但过去的这一年是那么的不可思议,那么的跌宕起伏、刻骨铭心,倘若能用一篇年终总结给这样的一年画上一个句号,便是再好不过的了。

+

年终总结 - 2020

准切地说,这篇年终总结来得有些晚了,晚了一个月十一天二十三小时五十九分钟。但过去的这一年是那么的不可思议,那么的跌宕起伏、刻骨铭心,倘若能用一篇年终总结给这样的一年画上一个句号,便是再好不过的了。

公元贰零贰零年,我来到这个世界的第十八年。年初,武汉疫情爆发,所有的一切都仿佛按下了暂停键,电影取消了,商场关闭了,学校放假了,就连那么多年来从未改变的高考时间也史无前例地推迟了。在这些与自己独处的漫长日夜里,很难得地,我慢慢开始和自己交流,和过去交流,执念被慢慢放下,伤痕开始逐渐愈合,记忆的回声终于不再萦绕,一切都在一个梦醒的早晨恢复正轨。年中,高考是生活赠与我的第一个成人礼,高考结束后的第二天,我结束了一个人面对生活的日子,我有了陪伴我、鼓励我、支持我、给我惊喜的女朋友,我收获了让我感到温暖而幸福的爱情,这是生活赠与我的第二个成人礼。第三个成人礼,是中大录取的通知讯息。当我看到短信的时候,我想到了过去三年我做的所有的事情,我想到了高考后的不甘,想到了她,想到了未来,我之后所有的生活都在那一刻,因为那一个短信而走向了一个崭新的方向。年末,我来到了大学,遇到了和蔼的老师,友好的舍友,我犹记得每一次和所爱之人见面时那种溢于言表的喜悦与激动,犹记得期末周复习的紧张与担心。而这个学期所有的故事,也都以虽不差但仍不尽如意的期末成绩画上了完整的句号。

过去这一年,我经历了悲伤,感受了快乐,收获了爱情,也开始了我成年后的第一年人生。2019快结束的时候,我认为生活已经不能更糟了,然而,2020开年时便驳倒了我这样的想法。但过完整整一年,虽然生活在很多时候变得更糟了,但在更多的时候,变得更加地与以往不一样了——我们认识到生命的宝贵,看到信仰的光芒与责任的力量,我们开始与身边的人联系起来,我们和家人们一起说话,和朋友散心,这一切,在2020都显得那么不一样。

可以说,从去年开始,我才真正开始生活。

转眼就要离开鼠年,希望我们所有人都能在新的一年,再出发。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2021/01/11/summary-2020/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

年终总结 - 2021

很快啊,又是一年过完了。
不知道什么时候开始,感觉一年的长度开始越来越短,短到年初念叨的想见的人到了年末也还是没有见,短到年初开始背的六级单词到了年末也没背下几个,短到年初那些信誓旦旦在今天看来不过又是明年一定…

+

年终总结 - 2021

很快啊,又是一年过完了。
不知道什么时候开始,感觉一年的长度开始越来越短,短到年初念叨的想见的人到了年末也还是没有见,短到年初开始背的六级单词到了年末也没背下几个,短到年初那些信誓旦旦在今天看来不过又是明年一定…

如果要用一个词来描述2021,我认为“改变”是最贴切不过的。

年初的时候,确诊了反流性食管炎。
本来以为会像以往的胃疼那样不过是短时间生活习惯的影响,过一阵子就会自己康复,但没想到即便是做了胃镜开了药也仍然没有阻止它一直折磨我到今天。为了缓解这个问题对生活的影响,很多习惯都不得不改变,其中就包括戒掉我喝了六七年的奶茶和几乎所有的甜食。
到了年中,就是转专业。
本是父母随心的一问,却没想让本来准备努力保研遥感的我重新思考了转专业的可能性,并在截止日期当天提交了申请。
在转专业的过程中发生了很多其他的事情,并且还要同时准备着转专业失败可能面临的绩点断崖下滑以及忍受每晚胃病的折磨,使得那一个月的每一天都无比难熬。
也正是在这个过程中我改变了对曾经很亲密的人的看法,并最终选择了去和平结束上一段关系。

除了这些明显的生活上的改变以外,今年更多的是一些想法和看法上的改变。

@@ -185,7 +185,7 @@ btf.addGlobalFn('pjaxSend', () => {

今天,过去的一年已成定局,新的一年就在我写下这些文字的时候就已然悄无声息地开始。如果要说对2022有什么展望的话…

我希望,
新的一年,
我和我所爱的所有人,
平安,健康,快乐。
就这样,就够了。

最后,
感谢过去的一年有你们陪伴。
真的,
谢谢你们。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2021/12/31/summary-2021/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

操统实验日志 第一章 序章

简述

+

操统实验日志 第一章 序章

简述

在一切开始之前,请允许我先简要地介绍一下关于这个实验的一切

它是关于什么的

@@ -506,7 +506,7 @@ btf.addGlobalFn('pjaxSend', () => {

目前为止,环境已经基本配置完成了
接下来就让我们开始愉快的操作系统实验之旅吧!

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2022/07/15/os-journal-vol-1/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

操统实验日志 第二章 万丈高楼平地起

关于本章

+

操统实验日志 第二章 万丈高楼平地起

关于本章

本章的将会首先介绍操作系统是如何运行起来的,并在此基础上介绍实现一个完备的操作系统实验需要实现哪些方面,以及这些部分的先后顺序和依赖关系

由于这份文档我并不打算作为一份完备的教程文档来编写,因此语言方面的介绍会相对简略或是跳过,对应的详细介绍可以参考学校的同步教程

在本章的后半部分,将会介绍MBR和中断的相关知识,记录如何编写MBR、测试使用BIOS启动MBR引导程序并通过中断输出字符串进行测试

@@ -953,7 +953,7 @@ BIOS从0x7C00处开始执行代码,其并不区分内存中存储 下一章中,将会编写bootloader,从mbr中加载bootloader并且启动,最后在bootloader中让CPU进入保护模式
如果准备好了的话,就让我们进入下一章吧!

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2022/07/15/os-journal-vol-2/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me
最新文章

操统实验日志 第三章 从实模式到保护模式

关于本章

+

操统实验日志 第三章 从实模式到保护模式

关于本章

在本章的第一部分中,将会介绍读取硬盘的CHS方式、LBA方式以及如何通过inout指令读写硬盘,之后会将上一章输出Hello World!的代码移植到BootLoader中,并且从MBR中加载并跳转到编写的BootLoader执行

第二部分中,会回顾保护模式的概念并介绍进入保护模式的四个步骤,并在开启保护模式之后输出第二个Hello World

第一次跃进:从MBR跳转到BootLoader

@@ -970,7 +970,7 @@ LoopCount &= SectorCount \times 512 \div 2 \\

完成
至此,就完成了本章的全部任务,赶紧使用make clean build run来测试代码的运行情况吧!

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2022/07/19/os-journal-vol-3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

操统实验日志 第四章 勇者之路

关于本章

+

操统实验日志 第四章 勇者之路

关于本章

在本章节的第一部分中,将会简要介绍在下一章中将要编写的KernelLoader,以及在开始着手进行它的编写之前所需要完成的,包括各种驱动、文件系统接口等在内的诸多准备工作。

在第一部分之后,我决定按照KernelLoader中的函数调用顺序,逐节完成KernelLoader中所需要的所有准备工作,因此在第二部分中,将会首先记录如何在项目中使用C语言和汇编混合编程,包括C语言是如何进行函数调用的,以及内联汇编中NASM向AT&T迁移语法所需要注意的问题。有了这部分基础知识,就可以进行第三部分编写一些常用的驱动,并从我个人的角度讲讲为什么要这么做,它对后续的代码编写能够起到哪些帮助。

在之后的第四部分中,会进行有关文件系统的知识的详述,并且带领大家阅读微软关于FAT文件系统的文档,根据文档完成FAT文件系统接口的设计和实现。

@@ -1845,7 +1845,7 @@ VirtualPageIndex &= VirtualPageAddress >> n \nonumber \\

勇者奖章
恭喜你,读完了所有日志中最长最复杂的一篇,后面的实验之路将会因这一章的努力而愈发平坦。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2022/08/20/os-journal-vol-4/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

年终总结 - 2022

2022.12.31 21:00
今年只剩下一场电影的时间了。

+

年终总结 - 2022

2022.12.31 21:00
今年只剩下一场电影的时间了。

过去一年,如果要用一个词语概括的话,那我想应当是“遇见”吧。

其实,关于这个词语,在过去的一年里的无数个瞬间,我都有过构想。
之所以选择“遇见”,因为遇见有着往前走的意思,当我们没有再停留在原地的时候,当我们不在回头看的时候,才会看见那些原本看不见的风景,遇见那些原本碰不到的人们。

过去的一年里,我确实有在向前走,如果更加贴切一些地说,应当是在向前狂奔。
年初的时候,我终于决定选择试着加入Matrix的大家庭,这个决定成为了我今年做得最正确的决定之一。在Matrix,我遇见了真正优秀,有梦想和追求的人们,我看到了与宿舍舍友待白昼如黑夜迥然不同的生活态度,我开始寻着前辈的步子,发展自己热爱的方向,向着自己曾经最仰慕的样子进步。
接着,我遇见了一个温柔,耐心而又体贴的女生。最初的遇见,本以为只会是寥寥几语就会最终沉没在微信记录中的一面之缘,但完全出乎意料地,这一面之缘让我们互相都成为了对方生命中最重要的人之一。这一次遇见,不但让我两点一线的生活再一次有了色彩,更是给过去的一年里留下了无数令我难忘的回忆。
在今年的五月份,我还在知乎收到了一份很长的留言,留言那头是我的初恋,留言是关于我曾经的一篇有关她的知乎回答。在我们分手后的很长一段时间里,那篇回答都是被我删除的状态,直到今年三月份的时候,我决定正视和放下我的过去,于是恢复了那一篇回答,她看到了,特地来感谢我。留言里讲述了一些分手以后的事情和她的近况,她也有了新的生活,新的恋人,希望我不要再因为过去而责怪自己。读完留言是泪流满面,三年过去了,大家都没选择憎恨对方,都没有留在原地,而是都踏入了新的环境,新的生活。
再后来,我搬到了D502,在那里遇见了后半年玩的最多的一只吉祥物,开启了接下来几乎大半年的实验室生活。D502可以说见证了我过去大半年的脚步,见证了我许多个难忘的项目,我的操作系统、计网小作业和我的图形学大作业等等等等。

@@ -185,7 +185,7 @@ btf.addGlobalFn('pjaxSend', () => {

感谢2022带给我的成长,回忆。

……

接下来,把时间交给2023,我们拭目以待。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2022/12/31/summary-2022/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

重生

时隔两年,终于借着重新配置家里网络环境的契机,重新搭建了这个博客。

+

重生

时隔两年,终于借着重新配置家里网络环境的契机,重新搭建了这个博客。

原先关于操作系统的文章正在慢慢搬迁,应该很快就能恢复了~

再一次启用关于自己的博客,感觉心里良多感慨。还记得上一次搭博客时的自己,刚来到计算机学院,对着网上的保姆教程在腾讯云的小机器上搭了 git 仓库、配置了宝塔面板、DNS 解析。

那时的自己对 TLS、证书、Git、反代、CDN、Docker 这些东西都还是那么陌生,以至于教程之外的东西完全不敢去碰,哪怕是在宝塔面板上配一个 Let’s Encrypt 的证书都要折腾好久,也没有去研究 hexo deploy 到底 deploy 了什么到服务端,只觉得能跑便是好事,这也就导致了后来的删库跑路事件——本地的博客仓库被主动删除,等到发现服务器上是没有 Markdown 源文件的时候已经太迟,由于没有了源文件,写新的博客势必会导致旧的 html 被覆盖,又因为文章实在太长迟迟没有动手迁移,原先的数万字长文就这样被冻在了旧的博客里长达两年。

@@ -182,7 +182,7 @@ btf.addGlobalFn('pjaxSend', () => {

不夸张地说,看到熟悉的页面再一次出现在浏览器中的时候,内心有许多感慨,大概就像离家的游子多年后重新推开家门时那样吧。拂去把手上的灰尘,推开门回到曾经熟悉的地方,所有的东西都还在原本的地方等着自己,仿佛从来没有离开过那样。博客大概就是我内心无处安放的杂思的归宿吧,我想,如今它们终于又能安家了。

这一次回来,不知道能够持续多久,但我希望,能够长一些、再长一些。至于内容,我也不打算维持早年纯技术的导向了,我更多地想让这个博客成为我存在的痕迹,让多年后的自己看到曾今的文章能够会想起当年的纠结、焦虑、喜悦或是激动,能够从这里,看到我。

总之,欢迎回家。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/11/reborn/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

基于 IPv6 公网地址、NAS 和 MacMini 的私有部署博客方案

方案速览

简单来说,方案分为了几个主要的部分:

+

基于 IPv6 公网地址、NAS 和 MacMini 的私有部署博客方案

方案速览

简单来说,方案分为了几个主要的部分:

  1. 根据 在 NAS 上部署自己的 Gitea 服务,无需公网服务器 方案打通外网到家用 NAS / MacMini 的链路
  2. 采用 Git 仓库 main 分支存放源码 + Gitea Actions 编译至 publish 分支实现源码及制品存储
  3. @@ -187,7 +187,7 @@ btf.addGlobalFn('pjaxSend', () => {

    环境准备

    在配博客之前,我是先配好了 Nas 上的 Gitea 服务,可以参考 在 NAS 上部署自己的 Gitea 服务,无需公网服务器 这一篇博客来准备基本的网络环境和 Gitea 服务。

    (也就是说,我是先搭好了 Gitea,然后实在不知道能拿干点什么,才决定把博客迁移回来的。有点为了醋包饺子的感觉哈哈,不过现在博客全部内容都运行在自己本地感觉还是颇有成就感的)

    仓库配置

    待后面补充~

    -
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/12/blog-from-scratch/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

短文 - 博客迁移小记

最开始想的迁移方案是使用 skip-render 标记 html,但始终觉得不够优雅,因为导航栏、个人信息、头图之类的内容时常都会变,如果 skip-render 那永远都会是当时那个版本的页面,甚至可能超链接都是失效的,除了能显示原本的博文之外其实体验应该是相当差的——横竖感觉就是很突兀嘛!

+

短文 - 博客迁移小记

最开始想的迁移方案是使用 skip-render 标记 html,但始终觉得不够优雅,因为导航栏、个人信息、头图之类的内容时常都会变,如果 skip-render 那永远都会是当时那个版本的页面,甚至可能超链接都是失效的,除了能显示原本的博文之外其实体验应该是相当差的——横竖感觉就是很突兀嘛!

直到今天突然意识到,hexo 渲染 markdown 为 html 文本肯定会分为三个大部分:

  1. 正文前部的各种元素,例如头图、导航栏、侧边栏等
  2. @@ -192,7 +192,7 @@ btf.addGlobalFn('pjaxSend', () => {

    然后再把原先的图片复制到现在的 img 目录下,批量改一手路径,Done!

    谁能想到这个卡了我两年的问题,竟然能 15 分钟就搞定了!!!

    过于激动,遂特写此文记录一下,真是拍大腿啊!!!

    -
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/13/blog-mig/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

在 NAS 上部署自己的 Gitea 服务,无需公网服务器

方案速览

简单来说,方案包含了以下几个主要部分:

+

在 NAS 上部署自己的 Gitea 服务,无需公网服务器

方案速览

简单来说,方案包含了以下几个主要部分:

  1. 公网访问使用光猫桥接路由器拨号,通过路由器同时获取 IPv4 大内网和 IPv6 公网 /64 地址
  2. DNS 解析通过 MacMini 上部署的 ddns-go 实现
  3. @@ -248,7 +248,7 @@ btf.addGlobalFn('pjaxSend', () => {

    不过,由于 Cloudflare 只支持有限的端口转发,并且只支持基于 http/https 协议的流量转发,因此适用面相对不那么广,但是家用部署网站服务还是绰绰有余了。具体允许的端口号见 Cloudflare 文档

    经过测试,中国联通封了 80,443,2096 端口,回源我采用的 2095 端口,这个在 Cloudflare 控制台 - 规则 - Origin Rules 可以创建回源规则针对特定域名指定。

    Gitea 安装与反向代理

    待后续翔实~

    -
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/13/host-git-at-home/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

短文 - 关于短博文的碎碎念

感觉如果博客只写长文的话,好像很快就会疲乏,其实很多时候想说的内容就是一两句话,即便硬是写成了长文,又觉得好像啰嗦了。

+

短文 - 关于短博文的碎碎念

感觉如果博客只写长文的话,好像很快就会疲乏,其实很多时候想说的内容就是一两句话,即便硬是写成了长文,又觉得好像啰嗦了。

看到 Hexo - Butterfly 有提供一个 “说说” 的页面可以用 .yml 格式来存一些说说文档,但是仔细一看发现好像不会自动分页,这样一来图片一多感觉加载就会变成彻底的灾难…

不知道为什么在静态编译的时候没有做成本地分页的格式呢… 就像文章那样,其实在编译阶段就可以分散到不同的 index.html 去了,好可惜,也许以后有空会想办法看看能不能改吧…

还有很多云存储的方案,但感觉把自己的内容放在云上,总感觉会比较担心数据安全和以后的迁移成本,纯本地的话哪怕一天发两条十年也不过才不到上万条数据,一个 .yml 就带走了,哎可惜没分页终究还是不打算去用。

想来想去,就把短博客也当作正常的文章一样的显示在主页吧,不过会在标题前面加上 “短文” 的标记和对应的 tag,也方便浏览的时候来做区分好了

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/13/micro-posts/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

短文 - 转正日

今天收到了转正邮件,正式标志着一个新的人生阶段的开始。

+

短文 - 转正日

今天收到了转正邮件,正式标志着一个新的人生阶段的开始。

对自己的期望就是,不要忘记做技术的初心,在新的阶段能有所成长,有所收获。

Po 一张在鹅厂的第一个关爱里程碑~

first_day

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/15/tencent-new-start/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

用 adb + app_process 执行 Java 代码 —— 一种无需安装 apk 的脱机代码执行方案

方案速览

本方案本质上是使用了安卓提供的 app_process 命令,在将 Java 代码正确地打包为需要的 .jar 或是 .dex 文件后,通过 app_process 启动对应的入口函数来实现 adb 执行 Java 代码的能力。

+

用 adb + app_process 执行 Java 代码 —— 一种无需安装 apk 的脱机代码执行方案

方案速览

本方案本质上是使用了安卓提供的 app_process 命令,在将 Java 代码正确地打包为需要的 .jar 或是 .dex 文件后,通过 app_process 启动对应的入口函数来实现 adb 执行 Java 代码的能力。

对于目标 .jar 或是 .dex 文件,有两种不同的编译方案:

  1. .dex 文件方式:
      @@ -272,7 +272,7 @@ btf.addGlobalFn('pjaxSend', () => {
    1. 在研究 atx/uiautomator2 的实现的时候,先入为主地就认为作者使用了 uiautomator <jar> 这种方式调用,后面注意力都放在了查找作者针对 u2.jar 的打包方式和相关代码上了,以至于在仓库里搜索 u2.jar 的时候竟然没有注意到在搜索结果中 core.py 赫然有着 command = "CLASSPATH=/data/local/tmp/u2.jar app_process / com.wetest.uia2.Main" 这一启动方式,与答案擦肩而过并且进一步在寻找 uiautomator 1.0 的打包方式上浪费了大半天的时间
    2. 对于一个陌生的仓库,如果想要了解它的源码实现,不一定非要自己去读,除了进行仓库里的关键词搜索,也可以去查找别人的源码解析,走个捷径
    -
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/20/jar-via-adb/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

短文 - 面对首次绩效考核的一些随笔

快要到年底绩效考核的时间了,回想过去半年好像一直忙忙碌碌但好像又没有什么很亮眼的成果。期间还有一大部分精力都投在了一个设计难度远大于实现难度的需求。

+

短文 - 面对首次绩效考核的一些随笔

快要到年底绩效考核的时间了,回想过去半年好像一直忙忙碌碌但好像又没有什么很亮眼的成果。期间还有一大部分精力都投在了一个设计难度远大于实现难度的需求。

回过头想想,其实在工作上开始做一件长期的事情之前,还是要去评估可行性、投入以及收效,到底这件事情有多大的优先级,是不是就值得现在立马开始投入人力去做这个。到底做些什么可以被业务感知到,从而去判断,到底哪些需求才是重要的,而不是一味的承接需求,最后反倒抓不到重点了。

当然,除此之外,更重要的一点是,在知道了公司存在强制的 Underperform 比例时,不免会想,也许有一天我也要成为背这个绩效的人?在这种场景下,是否不要把鸡蛋放在一个篮子里更为安全?诚然,上班本质还是一种利益交换,我给公司提供我能够产出的内容,帮助公司节省人力开支或是做原本人力做不了的事情,公司给我反馈大厂背景、经验、人脉和钱,当然,这里面必然伴随着剩余价值的剥削云云,但总的来看,也算是目前相对公平且不错的买卖,在这样的背景下,我还是会在工作的时候专注于需求本身,坚守住技术人应有的底线,但在工作时间之外,确实应该更加关注自身的投资,也许是身体的健康,也许是产品思维,也许真的去做一些能代表自己实力的东西吧。

前几天有看到一个帖子,个人认为说的其实很有道理,大致意思就是,人总要有一些能证明自己能力的东西,在校招的时候,学历就是最大的背书;在工作的时候,便是绩效和公司、项目经历。但是,如果能有足够的其他履历,比如大的开源项目、成功的产品,这些就会替代前者变成能力的证明,毕竟,选择前者只是没有后者的无奈之举。所以,我想,做自己感兴趣的事,并且在这方面出彩,大概才是最有价值的投资吧。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/11/08/first-grading/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me

长更 - Linux 常用指令

先挖坑,慢慢填

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/11/08/linux-commands/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

长更 - Linux 常用指令

先挖坑,慢慢填

+
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/11/08/linux-commands/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

Listary 命令分享 - 快捷 clone 仓库并使用 VSCode 打开

背景

日常工作中,经常会需要临时 Clone 某个仓库并且用 VSCode 打开,在 Windows 上我一般都是:

+

Listary 命令分享 - 快捷 clone 仓库并使用 VSCode 打开

背景

日常工作中,经常会需要临时 Clone 某个仓库并且用 VSCode 打开,在 Windows 上我一般都是:

  1. 用文件资源管理器定位到需要 Clone 到的位置然后右键呼出终端
  2. git clone
  3. @@ -198,7 +198,7 @@ btf.addGlobalFn('pjaxSend', () => {

    之后,在文件夹中直接输入 clode <repo url> [dest folder] 即可一键 clone

    [dest folder] 为选填,不填则是默认取 <repo url> 末尾仓库名称作为目标文件夹,同直接执行 git clone <repo url> 行为保持一致

    Settings

    -
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/11/19/listary-quick-clone-command/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

问题定位回顾 - Windows 上发起 tcp 连接时提示 Only one usage of each socket address (protocol/network address/port) is normally permitted

问题背景

某天在 Windows 宿主机上执行任务时,发现 wda 指令请求一直失败,查看日志发现唯一有效的错误日志是 Only one usage of each socket address (protocol/network address/port) is normally permitted,回顾宿主机环境在过去一段时间没有进行过变更,并且该问题是第一次出现,此前相同环境并没有出现过这个问题

+

问题定位回顾 - Windows 上发起 tcp 连接时提示 Only one usage of each socket address (protocol/network address/port) is normally permitted

问题背景

某天在 Windows 宿主机上执行任务时,发现 wda 指令请求一直失败,查看日志发现唯一有效的错误日志是 Only one usage of each socket address (protocol/network address/port) is normally permitted,回顾宿主机环境在过去一段时间没有进行过变更,并且该问题是第一次出现,此前相同环境并没有出现过这个问题

尝试在其他宿主机以及本地开发机上执行相同命令均不能稳定复现该问题,其中 Linux 开发机无法复现该问题

error info

定位过程

报错分析

由于报错信息只有一行,也只能从这个信息来入手。搜索引擎检索得到如下内容:

@@ -280,7 +280,7 @@ btf.addGlobalFn('pjaxSend', () => {

此处额外解释一下第二点中为什么又可以直接从容器内访问 usbmuxd 了:前面发现容器内请求 host.docker.internal 会让 docker 以 com.docker.backend.exe 的身份使用 127.0.0.1 发起一个代理的请求,这还正好满足了 usbmuxd 只监听 127.0.0.1:27015 的行为了,这样宿主机的反代服务也就不需要了。

对于 Linux 服务,如果需要保持架构上的统一,一个可能的优化方向是将 usbmuxd 监听的 socket 映射到容器内?考虑到 docker daemon 的 socket 可以做到,大概这个方向也可行,但目前还没有做尝试,就留作一些可行的方案放在此处吧~

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/11/22/debug-windows-socket-drain/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

年终总结 - 2023 至 2024

是因为太久没有写过叙事风格的文字,还是因为脑子早已经被快节奏的短视频侵蚀,又到了年末回顾的时候,我竟与面前的电脑周旋了一晚,最后愣是一句能读的话都没有写出来。末了,把写不出东西这个毛病归结于家里的环境缺乏情调,次日专门找了个海边的图书馆来写,结果到了太阳都下山了,才想起来自己此行的正事不是来这儿看书,而是写点什么。倒是歪打正着,好不容易来一趟,总得在关门之前写下点什么,遂匆匆留下此篇,作为我过去两年存在过的痕迹。

+

年终总结 - 2023 至 2024

是因为太久没有写过叙事风格的文字,还是因为脑子早已经被快节奏的短视频侵蚀,又到了年末回顾的时候,我竟与面前的电脑周旋了一晚,最后愣是一句能读的话都没有写出来。末了,把写不出东西这个毛病归结于家里的环境缺乏情调,次日专门找了个海边的图书馆来写,结果到了太阳都下山了,才想起来自己此行的正事不是来这儿看书,而是写点什么。倒是歪打正着,好不容易来一趟,总得在关门之前写下点什么,遂匆匆留下此篇,作为我过去两年存在过的痕迹。

2024 年的终曲轻轻落幕,与我二十一和二十二岁的许多事情一样,没有轰轰烈烈的收场,只是在那某一瞬间突然意识到,过去的时光就这么结束了。

是什么时候开始觉得时间过得很快的呢?
是那一天想喝家门口的喜茶,却发现它几个月前就已经搬走的时候吗?
是那个实习结束后的早晨伸着懒腰起床,打开手机看见日历上赫然写着 10 月的时候?
是考完研的那天午后,抱着沉沉的资料最后一次走出图书馆的瞬间?
是意识到自考研后又已经过去了两个 75 天的那天?
还是一转眼发现就连 2024 都已经过完的今天?

已经记不太清了。
只觉得像是一个阳光灿烂的下午,躺在阳台上被太阳晒得暖暖的椅子里眯上了眼睛。在梦里,时间很慢,慢到周一到周五的时间像有一个学期那么长,床头的耳塞堆成了小山,书页翻得泛黄。醒来的时候,冬日的阳光还照在脸上,窗外的鸟儿还在叽喳,楼下的商铺喇叭还播着广告,对面小区的女人也还在遛她的狗,一切都好像没变。只是家里客厅的台历不再写着 2023,楼下不再有开了两年的排骨饭,对面小区的萨摩也再没看到了。

@@ -194,7 +194,7 @@ btf.addGlobalFn('pjaxSend', () => {

那,选择另一条路,就会更好吗?

我永远也不会知道,在平行世界里,选择另一条路的自己,到底会不会过得更好。但可以肯定的是,在往后的日子里,在现在的道路上,我会好好过。

2025,向前走,别回头。

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/12/31/summary-2023-2024/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕

Coroutine 相关疑惑大模型问答记录

近日在看 python 的 asyncio 时突然想起了曾经对 C++ coroutine 以及 go 的 goroutine 的诸多未解疑惑。还记得当时有这些疑惑的时候,还没有 ChatGPT 或是 DeepSeek 这样方便的大模型解惑,反复查找各种资料最后因为时间原因还是没有深究,而如今 DeepSeek 已然如此发达,遂借此机会解答遗留在心中多年的疑惑,并以此贴记录。

+

Round 1

Q:
AsyncIO 或者说,Coroutine 是怎么实现,例如在 await 一个网络请求的时候,进程不会被内核挂起进入等待状态,而是可以继续正常被调度并且执行余下内容的?同时,当网络请求完成的时候,内核又是怎么让进程知道请求完成从而继续 await 后续未执行的内容的

+

A:

+
+

嗯,用户问的是AsyncIO或者说协程是怎么实现的,特别是在await一个网络请求的时候,进程不会被内核挂起,而是可以继续执行其他任务。然后当网络请求完成后,内核又是如何通知进程继续执行的。我需要详细解释AsyncIO和协程的工作原理,特别是事件循环、非阻塞I/O以及操作系统层面的机制,比如epoll或者kqueue。

+

首先,用户可能已经了解一些基本的异步编程概念,但想深入理解底层机制。需要分步骤解释,从协程、事件循环到非阻塞I/O和操作系统接口。可能需要提到用户态和内核态的区别,以及如何通过事件驱动的方式处理I/O操作,避免阻塞。

+

然后,用户可能对内核如何通知事件完成感兴趣,这里需要解释I/O多路复用机制,比如select、poll、epoll,特别是epoll在Linux中的工作方式。当有I/O事件发生时,内核如何通知应用程序,从而触发回调函数,恢复协程执行。

+

另外,需要区分进程、线程和协程的不同。协程是用户态的轻量级线程,由事件循环调度,而不是由操作系统内核调度。这样在遇到I/O操作时,可以挂起当前协程,执行其他任务,提高并发性能。

+

可能还需要提到Future和Task对象,在AsyncIO中如何管理异步操作的状态,以及await关键字如何挂起协程,让出控制权给事件循环,直到Future完成。

+

最后,总结整个流程,从发起网络请求到事件循环注册,到操作系统处理I/O,完成后的回调,恢复协程执行。确保解释清晰,技术细节准确,同时避免过于晦涩。

+
+

AsyncIO/Coroutine 的实现核心在于用户态协作式调度操作系统非阻塞I/O机制的结合。以下是技术实现的关键点:

+
+

一、核心机制

    +
  1. 事件循环 (Event Loop)
    通过单线程轮询I/O事件,使用epoll(Linux)/kqueue(BSD)/IOCP(Windows)等系统调用实现高效事件监听:

    +
    1
    2
    3
    4
    5
    6
    # 伪代码示例
    while True:
    events = epoll_wait(epoll_fd, timeout)
    for fd, event in events:
    callback = fd_to_callback[fd]
    callback()
    +
  2. +
  3. 非阻塞I/O
    通过fcntl(fd, F_SETFL, O_NONBLOCK)设置socket为非阻塞模式,当I/O未就绪时立即返回EWOULDBLOCK错误,避免进程被挂起。

    +
  4. +
  5. 协程调度
    使用生成器(Python 3.5+的async/await)实现协程切换:

    +
    1
    2
    async def fetch():
    await socket.read() # 让出控制权
  6. +
+
+

二、内核交互细节

    +
  1. I/O准备阶段

    +
    graph LR
    +A[发起socket.read()] --> B[注册到epoll]
    +B --> C[挂起当前协程]
    +C --> D[事件循环继续运行其他协程]
    +
  2. +
  3. 内核通知机制
    当数据到达时,网卡通过中断通知内核,内核将socket标记为就绪,epoll会检测到该事件并通知用户态。

    +
  4. +
  5. 回调触发
    事件循环收到内核通知后,通过callback()恢复对应协程的上下文继续执行。

    +
  6. +
+
+

三、性能关键指标

    +
  1. 上下文切换成本

    + + + + + + + + + + + + + + + + + + + +
    类型切换成本
    进程切换~1-10μs
    线程切换~0.5-2μs
    协程切换~0.1-0.3μs
    +
  2. +
  3. I/O吞吐量对比

    +
    1
    \text{Throughput} = \frac{\text{Requests}}{\text{Context Switch Time} + \text{I/O Latency}}
  4. +
+
+

四、操作系统支持

    +
  1. Linux epoll
    使用红黑树管理文件描述符,时间复杂度为$O(1)$的事件通知。

    +
  2. +
  3. BSD kqueue
    支持更多事件类型过滤,但API更复杂。

    +
  4. +
  5. Windows IOCP
    真正的异步I/O模型,与Overlapped I/O配合使用。

    +
  6. +
+
+

五、编程模型对比

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
特性多进程多线程协程
内存占用
切换成本极低
并发能力
数据共享困难需同步天然共享
+
+

六、典型工作流程

    +
  1. 协程调用await socket.read()
  2. +
  3. 事件循环将socket注册到epoll
  4. +
  5. 当前协程挂起,保存上下文(PC/寄存器/栈)
  6. +
  7. 事件循环继续执行其他协程
  8. +
  9. 内核通知数据到达,epoll返回就绪事件
  10. +
  11. 事件循环调用注册的回调函数
  12. +
  13. 恢复挂起协程的上下文,继续执行
  14. +
+

这种设计使得单线程即可实现数万并发连接(如Nginx的C10K解决方案),同时避免了内核态/用户态频繁切换的开销。

+

Round 2

Q:
协程是否需要保存当前栈帧

+

A:

+
+

好的,用户问协程是否需要保存当前栈帧。我需要先回顾之前关于协程和AsyncIO的讨论,确保回答的一致性。用户可能已经了解协程的基本原理,现在想深入技术细节。

+

首先,协程的挂起和恢复确实需要保存栈帧。因为协程在用户态切换,没有内核的调度,所以必须手动保存和恢复上下文。栈帧保存了局部变量、函数调用链等信息,这对恢复执行至关重要。

+

接下来,我需要解释栈帧保存的具体内容,比如寄存器状态、局部变量、返回地址等。可能需要用例子说明,比如Python的生成器如何保存状态。同时,要区分不同语言的实现,比如Go的goroutine使用分段栈,而Python的协程可能依赖生成器。

+

用户可能还想知道为什么需要保存这些信息,以及不保存会有什么后果。比如,如果栈帧不保存,恢复时无法继续执行,导致数据错误或崩溃。此外,栈的动态增长如何处理,比如是否使用协程私有栈,避免栈溢出影响其他协程。

+

还要考虑性能方面,栈帧保存的开销如何,比如对比线程的上下文切换。可能提到协程切换的高效性,因为只在用户态操作,无需陷入内核。

+

另外,用户可能对具体实现机制感兴趣,比如setjmp/longjmp、ucontext库或者汇编手动切换上下文。不同语言和库的实现方式不同,但核心都是保存和恢复栈帧。

+

最后,需要总结栈帧保存的必要性,强调这是协程能够正确挂起和恢复的关键,确保用户理解其重要性。

+
+

协程的实现必须完整保存当前栈帧,这是实现正确挂起/恢复的核心机制。栈帧保存的完整性和实现方式直接决定了协程的性能和可靠性,具体技术细节如下:

+
+

一、栈帧保存的必要性

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
组件必须保存的原因丢失后果
返回地址确定恢复执行的位置程序计数器错乱导致崩溃
局部变量维护函数调用上下文数据不一致或计算错误
寄存器状态保证指令连续执行寄存器污染导致未定义行为
调用链信息维护协程嵌套调用关系栈回溯失败,调试信息丢失
+
+

二、栈帧保存的实现方式

1. 轻量级方案(Python等解释型语言)

1
2
3
4
5
6
7
8
9
10
11
# 生成器协程的栈帧保存
def coro():
x = 42
yield # 此处保存栈帧:
# - 局部变量x
# - 指令指针(IP)
# - 当前栈指针(SP)
print(x)

gen = coro()
next(gen) # 触发栈帧保存
+

特点

+
    +
  • 通过代码对象(code)和帧对象(f_lasti)记录状态
  • +
  • 每个yield消耗约200字节内存
  • +
+

2. 高性能方案(C/C++/Rust)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用ucontext库手动切换
void coroutine_entry(void* arg) {
while (true) {
// 业务逻辑
swapcontext(&ctx_current, &ctx_scheduler);
}
}

// 上下文切换时自动保存:
// - 所有通用寄存器 (rax, rbx...)
// - 浮点寄存器 (xmm0-15)
// - 栈指针 (rsp)
// - 指令指针 (rip)
// - 标志寄存器 (rflags)
+

性能指标

+
    +
  • 单次切换约23ns (x86) / 35ns (ARM)
  • +
  • 内存开销约2KB/协程
  • +
+

3. 零拷贝方案(Go goroutine)

1
2
3
4
5
6
7
8
9
// Go使用分段栈实现协程
func coro() {
var big [8192]byte // 触发栈扩容
// ...
}

// 运行时自动管理:
// - 栈扩容时复制到新内存区域
// - 使用连续栈时维护拷贝指针
+

优势

+
    +
  • 栈空间自动增长,无最大限制
  • +
  • 栈切换仅需修改寄存器,无需内存拷贝
  • +
+
+

三、栈帧保存的底层原理

1. 寄存器级保存

1
2
3
4
5
6
7
8
9
10
11
12
; x86-64上下文保存示例
swapcontext:
push rbp
mov rbp, rsp
push rbx
push r12
push r13
push r14
push r15
mov [rdi + UC_MCONTEXT_GREGS + REG_RSP], rsp
mov [rdi + UC_MCONTEXT_GREGS + REG_RIP], rcx
; 共保存23个寄存器和浮点状态
+ +

2. 栈内存管理

1
2
3
4
5
6
7
8
9
10
11
12
13
// 协程私有栈实现
struct coroutine {
void* stack; // 独立栈空间
size_t stack_size;
jmp_buf env; // 保存的寄存器集合
};

// 切换时重置栈指针
void switch_to(struct coroutine* from, struct coroutine* to) {
_switch(&from->env, &to->env);
// 汇编实现栈指针切换:
// mov rsp, [to->env + REG_RSP]
// ret
+ +

3. 栈溢出防护

1
2
3
4
5
6
7
8
9
// Rust async-std的栈保护机制
async fn deep_recursion() {
let guard = PageGuard::new(); // 分配保护页
recursive_call().await;
}

// 内存布局:
// [ 可用栈空间 | RED ZONE (不可访问) | 保护页 ]
// 访问保护页触发SIGSEGV,自动扩容栈
+ +
+

四、不同语言的实现对比

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
语言栈帧保存方式切换开销最大协程数 (8GB内存)
Python生成器帧对象120ns~500,000
Go分段栈/连续栈35ns~10,000,000
C++ucontext/手动汇编23ns~5,000,000
Java(Loom)虚拟线程栈镜像45ns~2,000,000
Rustasync/await状态机编译时展开18ns~8,000,000
+
+

五、特殊场景处理

    +
  1. 动态栈扩展
    当协程调用深度递归时:

    +
    1
    2
    3
    4
    5
    6
    7
    void coro() {
    char buf[1024];
    await read(); // 栈空间不足时:
    // 1. 分配新栈
    // 2. 拷贝旧栈数据
    // 3. 修改栈指针
    }
    +
  2. +
  3. 跨栈引用保护
    C++20协程要求:

    +
    1
    2
    3
    4
    5
    6
    task<void> unsafe_coro() {
    int local = 42;
    co_await something(); // 挂起点之后
    print(local); // 可能引用已释放栈!
    }
    // 正确做法:使用shared_ptr持有跨栈数据
    +
  4. +
  5. 垃圾回收协同
    Go的逃逸分析确保协程栈安全:

    +
    1
    2
    3
    4
    5
    6
    func coro() {
    x := 42
    go func() {
    println(x) // x逃逸到堆
    }()
    }
  6. +
+
+

结论

协程必须完整保存当前栈帧,这是实现正确执行流控制的基石。现代运行时通过寄存器快照、独立栈空间、智能内存管理等技术,在保证安全性的同时将切换开销控制在纳秒级。不同语言根据自身特性选择最优实现策略,如Go的分段栈、Rust的零成本抽象等,最终目标都是在用户态实现接近线程的编程模型,同时获得更高的并发性能。

+

Round 3

Q:
我好奇的一点是:如果函数 A 中调用了 b = B(),而 B 中调用了 await post_http_request(),我理解在 post_http_request 中会在发送完 http 请求后对 post_http_request 函数的栈帧内所有内容进行保存,然后返回到 B 中,由于 B 中调用了 await,则会保存 B 的栈帧,最后返回到 A 中继续执行,对吗?

+

我的疑问在于,在你前面给出的例子中,只保存了寄存器相关的值而没有保存栈上存储的内容,如果在这种情况下,A 函数继续执行,可能会覆盖原先 Bpost_http_request 栈上的内容,这是否会导致切换失败?还是说,栈帧保存也会保存栈上的数据?这样不会消耗额外的时间来拷贝内存吗?

+

A:

+
+

嗯,用户的问题是关于协程在挂起时如何保存栈帧的。他举了一个例子,函数A调用B,B中调用了await post_http_request()。用户想知道,当post_http_request发送完HTTP请求后,是否会保存它的栈帧,然后返回到B,再保存B的栈帧,最后回到A继续执行。用户担心如果只保存寄存器而没有保存栈上的数据,A继续执行可能会覆盖B和post_http_request的栈内容,导致切换失败。或者是否需要保存栈上的数据,这样会不会有内存拷贝的开销。

+

首先,我需要回顾之前关于协程保存栈帧的讨论。之前提到协程必须保存当前栈帧,包括寄存器、局部变量等。用户可能误解了栈帧保存的方式,认为只保存寄存器而没有保存栈上的数据,但实际上,正确的实现需要保存整个栈帧的内容,或者使用独立的栈空间来避免被覆盖。

+

接下来,需要解释协程的不同实现方式。比如,在Python这样的生成器协程中,每个协程可能共享同一个栈,通过生成器对象保存局部变量和指令指针,但可能不保存整个栈帧。而在其他语言如Go或Rust中,每个协程可能有自己的独立栈,切换时切换栈指针,这样就不会被其他协程覆盖。

+

用户提到的例子中,当post_http_request被await挂起时,协程需要保存当前的执行状态,包括B函数和post_http_request的栈数据。如果所有协程共享同一个栈,那么当A继续执行时,确实可能覆盖之前的栈内容,导致数据损坏。因此,正确的实现需要每个协程有自己的栈空间,或者在挂起时将栈数据拷贝到堆中保存。

+

需要解释不同语言的实现策略。例如,Go使用分段栈或连续栈,每个goroutine有自己的栈,切换时只需切换栈指针,无需拷贝数据。而Python的协程可能使用生成器,保存必要的状态而非整个栈,适用于浅层调用,但深层嵌套可能有问题。

+

用户担心内存拷贝的开销,需要说明高性能实现(如Go、Rust)如何通过独立栈来避免拷贝,只在必要时增长栈或分配新栈,从而减少开销。同时,解释栈切换的机制,比如修改栈指针寄存器,使得每个协程使用不同的内存区域,互不干扰。

+

可能还需要提到,当协程挂起时,其栈帧的数据必须保留在内存中,直到恢复执行。如果使用独立栈,这些数据不会被其他协程覆盖,因此无需拷贝。而如果共享栈,则需要将栈数据保存到堆中,这会带来拷贝开销,但现代实现通常避免这种方式,选择为每个协程分配独立栈空间。

+

总结用户的疑问点:是否保存栈数据,如何避免覆盖,以及性能影响。需要明确回答,协程实现中栈帧的保存方式,不同策略的优缺点,以及如何解决用户提到的覆盖问题。

+
+

协程的实现中,栈帧保存机制是保证正确性的核心,但具体实现方式在不同语言/框架中有显著差异。针对你的场景,我们分层次解析:

+
+

一、场景还原与问题本质

1
2
3
4
5
async def A():
b = await B() # <- 挂起点1

async def B():
await post_http_request() # <- 挂起点2
+ +

当执行到post_http_request()中的await时:

+
    +
  1. 调用链A → B → post_http_request
  2. +
  3. 挂起顺序:从post_http_request开始逐层向上传递await
  4. +
  5. 关键问题:如何保存整个调用链的上下文,避免栈数据被覆盖?
  6. +
+
+

二、两种核心实现策略

1. 共享栈模式 (Stackful Coroutines)

    +
  • 代表实现:Go goroutine、C++ Boost.Coroutine
  • +
  • 核心机制
      +
    • 每个协程拥有独立栈空间(初始大小通常为2KB-8KB)
    • +
    • 切换时直接切换栈指针寄存器(如x86的rsp
    • +
    • 无需拷贝栈数据,通过内存隔离避免覆盖
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
// Go调度器伪代码
func schedule() {
// 保存当前协程的rsp
old_gobuf.rsp = get_rsp()
// 切换到目标协程的栈
set_rsp(new_gobuf.rsp)
ret
}
+ +
    +
  • 内存布局

    +
    1
    2
    3
    4
    5
    ┌─────────────┐
    │ 协程X的栈 │ ← rsp_x
    ├─────────────┤
    │ 协程Y的栈 │ ← rsp_y
    └─────────────┘
    +
  • +
  • 优势:零拷贝,切换极快(约35ns)

    +
  • +
  • 代价:每个协程需预分配独立栈空间

    +
  • +
+

2. 状态机模式 (Stackless Coroutines)

    +
  • 代表实现:Python AsyncIO、Rust async/await
  • +
  • 核心机制
      +
    • 通过编译器将协程转换为状态机
    • +
    • 所有局部变量提升到堆分配的结构体
    • +
    • 栈帧数据显式保存到堆内存
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
// Rust async编译后伪代码
struct B_Future {
state: u32,
local_vars: B_Locals, // 包含所有局部变量
}

struct A_Future {
b_future: B_Future,
local_b: Option<B_Result>,
}
+ +
    +
  • 内存布局

    +
    1
    2
    3
    4
    5
    ┌─────────────┐
    │ 主线程栈 │ ← 仅存状态机指针
    ├─────────────┤
    │ 堆内存 │ ← 存储所有协程状态
    └─────────────┘
    +
  • +
  • 优势:内存占用低(无独立栈)

    +
  • +
  • 代价:局部变量访问需间接寻址(约多1-3 CPU周期)

    +
  • +
+
+

三、关键问题解答

Q1:为何不会发生栈数据覆盖?

    +
  • 共享栈模式:每个协程有独立栈空间,物理内存不重叠
  • +
  • 状态机模式:所有协程共享主线程栈,但局部变量已提升到堆,栈上仅存指针
  • +
+

Q2:栈数据是否需要显式拷贝?

    +
  • 共享栈模式:无需拷贝,切换时直接跳转栈指针
  • +
  • 状态机模式:编译器自动将局部变量”移动”到堆结构体,相当于隐式拷贝
  • +
+

Q3:性能影响对比

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
指标共享栈模式状态机模式
单次切换延迟~35ns~18ns
内存开销/协程2KB-8KB200B-2KB
局部变量访问速度直接栈访问间接堆访问
最大协程数 (8GB)~1,000,000~10,000,000
+
+

四、具体语言实现剖析

1. Python AsyncIO

    +
  • 实现方式:生成器+状态机
  • +
  • 栈保存机制
      +
    • 每个async def函数转换为生成器
    • +
    • 局部变量保存在生成器对象的gi_frame
    • +
    • 不保存完整栈,依赖事件循环调度
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async def B():
x = 42 # 存储在生成器对象的x属性
await post_http_request()
print(x) # 从生成器对象读取x

# 编译后伪代码
class B_Generator:
def __init__(self):
self.x = None
self.state = 0

def __next__(self):
if self.state == 0:
self.x = 42
self.state = 1
return post_http_request()
elif self.state == 1:
print(self.x)
+ +
    +
  • 缺陷:无法处理深层递归调用(栈深度受限)
  • +
+

2. Rust async/await

    +
  • 实现方式:状态机 + 零成本抽象
  • +
  • 栈保存机制
      +
    • 编译器将每个async fn转换为结构体
    • +
    • 所有局部变量成为结构体字段
    • +
    • 通过Pin保证内存位置固定
    • +
    +
  • +
+
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
async fn B() {
let x = 42; // 存储在B_Future结构体
post_http_request().await;
println!("{}", x);
}

// 编译后伪代码
struct B_Future {
state: u32,
x: i32,
http_request: PostHttpRequestFuture,
}

impl Future for B_Future {
fn poll(...) {
match self.state {
0 => {
self.x = 42;
self.state = 1;
self.http_request.poll()
}
1 => {
println!("{}", self.x);
Poll::Ready(())
}
}
}
}
+ +
    +
  • 优势:无堆分配(可通过no_std运行)
  • +
+
+

五、实际内存布局示例

场景:A → B → post_http_request挂起

    +
  • **共享栈模式 (Go)**:

    +
    1
    2
    3
    4
    5
    ┌─────────────┐
    │ 协程A栈 │ ← 保存B的返回地址和局部变量
    ├─────────────┤
    │ 协程B栈 │ ← 保存post_http_request的上下文
    └─────────────┘
    +
      +
    • 每个协程栈独立,切换时硬件自动隔离
    • +
    +
  • +
  • **状态机模式 (Rust)**:

    +
    1
    2
    3
    4
    5
    ┌─────────────┐
    │ 主栈 │ → 仅存储当前活跃Future指针
    ├─────────────┤
    │ 堆内存 │ ← A_Future { B_Future { PostHttpRequest_Future } }
    └─────────────┘
    +
      +
    • 所有状态嵌套存储在堆上
    • +
    +
  • +
+
+

六、总结

    +
  • 共享栈协程通过独立栈空间物理隔离数据,无需拷贝但预分配内存
  • +
  • 状态机协程通过编译期代码变换将栈数据提升到堆,牺牲局部变量访问速度换取高密度
  • +
  • 现代语言(如Go/Rust)通过精细设计,使得协程切换开销可控制在20-50ns,完全满足高并发需求
  • +
+

Round 4

Q:
所以你的意思是协程函数会有自己独立的栈空间是吗?这样的话,调用协程函数是否是和调用不同函数时不同,例如需要额外切换 rsp 到对应协程函数所在的栈上?

+

A:

+
+

好的,我现在需要回答用户关于协程函数是否拥有独立栈空间以及调用协程函数是否需要切换栈指针的问题。用户之前的对话已经涉及协程的实现机制,包括共享栈和状态机模式的区别,以及栈帧保存的必要性。用户现在想确认协程是否使用独立栈空间,以及调用时是否需要切换栈指针。

+

首先,我需要回顾之前的讨论。用户之前的问题集中在协程如何保存栈帧,避免数据被覆盖。我的回答提到了共享栈和状态机两种模式,其中共享栈模式每个协程有独立栈空间,而状态机模式则通过编译器将局部变量提升到堆中。用户现在的问题直接询问协程是否有独立栈空间,以及是否需要切换rsp寄存器。

+

接下来,我需要明确回答用户的问题:是的,某些协程实现(如共享栈模式)确实使用独立栈空间,并在调用时切换rsp。而状态机模式(如Rust的async/await)则不依赖独立栈,而是将状态保存在堆上,因此不需要切换栈指针。

+

需要分点详细解释两种模式的区别,特别是栈的使用和切换机制。同时,要举例说明不同语言中的实现,如Go的goroutine使用共享栈,而Rust使用状态机模式。此外,需要比较两者的优缺点,如性能、内存占用和适用场景。

+

可能还需要涉及技术细节,如栈指针切换的具体操作,如何通过修改rsp寄存器实现协程切换,以及状态机模式如何避免栈切换。同时,要解答用户可能的疑问,比如为什么有的实现选择共享栈,有的选择状态机,各自的权衡是什么。

+

需要确保回答准确,避免错误。例如,Go的协程确实使用分段栈或连续栈,而Rust的async/await在编译时转换为状态机,不依赖独立栈空间。同时,要指出切换栈指针的性能影响,以及状态机模式在内存使用上的优势。

+

最后,总结两种模式的适用场景,帮助用户理解不同协程实现的优缺点,以便根据具体需求选择合适的模型。

+
+

协程是否拥有独立栈空间取决于具体的实现模型,不同编程语言和框架采用了不同的策略。以下是两种主流实现方式的详细对比:

+
+

一、共享栈协程 (Stackful Coroutines)

代表实现:Go goroutine、Lua coroutine、C++ Boost.Coroutine
核心特性:每个协程拥有独立的栈空间,切换时需要修改栈指针寄存器(如x86的rsp)。

+

1. 栈空间管理

    +
  • 独立栈分配
    1
    2
    3
    4
    // Go运行时为每个goroutine预分配栈(初始2KB)
    go func() { // 创建新协程
    myFunc() // 在独立栈中运行
    }()
  • +
  • 栈增长机制
      +
    • 分段栈(旧版Go):栈不足时分配新段,通过morestack检测
    • +
    • 连续栈(现代Go):栈不足时整体复制到更大的内存区域
    • +
    +
  • +
+

2. 栈指针切换

    +
  • 切换原理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ; x86-64协程切换伪代码
    swapcontext:
    push rbp
    mov rbp, rsp
    ; 保存当前寄存器到旧协程上下文
    mov [old_ctx + RSP_OFFSET], rsp
    ; 加载新协程的栈指针
    mov rsp, [new_ctx + RSP_OFFSET]
    ; 恢复新协程的寄存器
    pop rbp
    ret
  • +
  • 性能指标
      +
    • 切换耗时:约35-50ns(包括保存/恢复16个通用寄存器+浮点寄存器)
    • +
    • 内存开销:每个协程至少2KB(Go) 到 8KB (C++)
    • +
    +
  • +
+

3. 调用链示例

1
2
3
4
5
6
7
func A() {
B() // 切换到B的栈
}

func B() {
C() // 切换到C的栈
}
+

内存布局

+
1
2
3
4
5
6
7
┌─────────────┐
│ 主线程栈 │ ← 0x7ffeefbff000
├─────────────┤
│ Goroutine A栈 │ ← 0xc000032000
├─────────────┤
│ Goroutine B栈 │ ← 0xc0000ac000
└─────────────┘
+ +
+

二、无栈协程 (Stackless Coroutines)

代表实现:Rust async/await、Python AsyncIO、JavaScript async
核心特性:协程共享主线程栈,通过状态机管理上下文,无需切换栈指针。

+

1. 实现原理

    +
  • 状态机转换
    1
    2
    3
    4
    5
    async fn example() {
    let a = 1; // 状态0
    some_io().await; // 状态1
    println!("{}", a);
    }
    +编译后展开为:
    1
    2
    3
    4
    5
    struct ExampleFuture {
    state: u32,
    a: i32,
    io_future: SomeIOFuture,
    }
  • +
  • 栈共享机制
      +
    • 所有局部变量存储在堆上的Future对象中
    • +
    • 主线程栈仅保留当前活跃Future的指针
    • +
    +
  • +
+

2. 内存布局

1
2
3
4
5
6
7
8
9
10
┌─────────────┐
│ 主线程栈 │
│-------------│ ← 仅存储临时变量和返回地址
│ Future对象指针 →
├─────────────┤
│ 堆内存 │
│-------------│ ← Future对象包含所有协程状态
│ a=1 │
│ io_future │
└─────────────┘
+ +

3. 性能对比

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
指标共享栈协程无栈协程
单次切换开销~35ns~18ns
内存开销/协程2KB-8KB200B-2KB
最大协程数 (8GB)~1,000,000~10,000,000
局部变量访问直接栈访问间接堆访问
+
+

三、关键问题解答

Q1:协程调用是否需要切换栈指针?

    +
  • 共享栈协程需要显式切换rsp寄存器
    1
    2
    ; Go调度器切换goroutine时的关键操作
    MOVQ gobuf_sp(BX), SP // 加载新协程的栈指针
  • +
  • 无栈协程无需切换,所有状态在堆上管理
  • +
+

Q2:如何避免栈数据覆盖?

    +
  • 共享栈:物理内存隔离,每个协程栈独立分配
    1
    2
    3
    4
    5
    // Go连续栈扩容时的复制操作
    old_stack = current_stack;
    new_stack = malloc(new_size);
    memcpy(new_stack, old_stack, old_size);
    free(old_stack);
  • +
  • 无栈协程:局部变量存储在堆对象中,与调用链解耦
  • +
+

Q3:为何无栈协程性能更高?

    +
  • 寄存器压力小:无需保存/恢复完整寄存器上下文
  • +
  • 缓存友好:状态机结构通常更紧凑,提高缓存命中率
  • +
  • 零内存拷贝:状态切换通过指针跳转完成
  • +
+
+

四、语言实现对比

1. Go (共享栈)

    +
  • 栈增长策略
      +
    • 初始2KB,每次翻倍直至32KB,之后按25%增长
    • +
    • 栈收缩机制防止内存泄漏
    • +
    +
  • +
  • 调度优势
    1
    2
    // Go调度器在系统线程间均衡goroutine
    runtime.GOMAXPROCS(4) // 使用4个OS线程
  • +
+

2. Rust (无栈)

    +
  • 零成本抽象
    1
    2
    3
    4
    5
    6
    7
    8
    // async函数编译为状态机,无额外运行时开销
    let fut = async { 1 };
    // 等效于:
    struct MyFuture { /* 状态字段 */ }
    impl Future for MyFuture {
    type Output = i32;
    fn poll(...) { ... }
    }
  • +
  • 无堆分配优化
    1
    2
    3
    4
    5
    // 通过`#[async_trait]`实现动态分发
    #[async_trait]
    trait MyTrait {
    async fn method(&self);
    }
  • +
+

3. Python (生成器式无栈)

    +
  • 生成器协程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    async def coro():
    await asyncio.sleep(1)
    # 转换为:
    class Coro:
    def __init__(self):
    self.state = 0
    def __next__(self):
    if self.state == 0:
    self.state = 1
    return sleep(1)
  • +
  • 缺陷:无法深度递归(受解释器栈限制)
  • +
+
+

五、总结

    +
  • 共享栈协程:通过独立栈空间和rsp切换实现真并发,适合需要深度递归或复杂调用链的场景,以Go为代表。
  • +
  • 无栈协程:通过状态机和堆存储实现高密度协程,以Rust/Python为代表,适合I/O密集型和高并发场景。
  • +
  • 选择依据
      +
    • 需要高性能和可控内存 → Rust无栈协程
    • +
    • 需要简单并发模型和深度调用 → Go共享栈协程
    • +
    • 快速开发和高层抽象 → Python无栈协程
    • +
    +
  • +
+

两种模型各有优劣,现代运行时(如.NET)甚至支持混合模式,开发者应根据具体需求选择最合适的协程实现。

+
文章作者: Linloir
文章链接: https://blog.linloir.cn/2025/03/17/coroutine-llm-qa/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me
目录
  1. 1. Round 1
    1. 1.1. 一、核心机制
    2. 1.2. 二、内核交互细节
    3. 1.3. 三、性能关键指标
    4. 1.4. 四、操作系统支持
    5. 1.5. 五、编程模型对比
    6. 1.6. 六、典型工作流程
  2. 2. Round 2
    1. 2.1. 一、栈帧保存的必要性
    2. 2.2. 二、栈帧保存的实现方式
      1. 2.2.1. 1. 轻量级方案(Python等解释型语言)
      2. 2.2.2. 2. 高性能方案(C/C++/Rust)
      3. 2.2.3. 3. 零拷贝方案(Go goroutine)
    3. 2.3. 三、栈帧保存的底层原理
      1. 2.3.1. 1. 寄存器级保存
      2. 2.3.2. 2. 栈内存管理
      3. 2.3.3. 3. 栈溢出防护
    4. 2.4. 四、不同语言的实现对比
    5. 2.5. 五、特殊场景处理
    6. 2.6. 结论
  3. 3. Round 3
    1. 3.1. 一、场景还原与问题本质
    2. 3.2. 二、两种核心实现策略
      1. 3.2.1. 1. 共享栈模式 (Stackful Coroutines)
      2. 3.2.2. 2. 状态机模式 (Stackless Coroutines)
    3. 3.3. 三、关键问题解答
      1. 3.3.1. Q1:为何不会发生栈数据覆盖?
      2. 3.3.2. Q2:栈数据是否需要显式拷贝?
      3. 3.3.3. Q3:性能影响对比
    4. 3.4. 四、具体语言实现剖析
      1. 3.4.1. 1. Python AsyncIO
      2. 3.4.2. 2. Rust async/await
    5. 3.5. 五、实际内存布局示例
      1. 3.5.1. 场景:A → B → post_http_request挂起
    6. 3.6. 六、总结
  4. 4. Round 4
    1. 4.1. 一、共享栈协程 (Stackful Coroutines)
      1. 4.1.1. 1. 栈空间管理
      2. 4.1.2. 2. 栈指针切换
      3. 4.1.3. 3. 调用链示例
    2. 4.2. 二、无栈协程 (Stackless Coroutines)
      1. 4.2.1. 1. 实现原理
      2. 4.2.2. 2. 内存布局
      3. 4.2.3. 3. 性能对比
    3. 4.3. 三、关键问题解答
      1. 4.3.1. Q1:协程调用是否需要切换栈指针?
      2. 4.3.2. Q2:如何避免栈数据覆盖?
      3. 4.3.3. Q3:为何无栈协程性能更高?
    4. 4.4. 四、语言实现对比
      1. 4.4.1. 1. Go (共享栈)
      2. 4.4.2. 2. Rust (无栈)
      3. 4.4.3. 3. Python (生成器式无栈)
    5. 4.5. 五、总结
最新文章
\ No newline at end of file diff --git a/archives/2019/12/index.html b/archives/2019/12/index.html index 0827b44..5fd905b 100644 --- a/archives/2019/12/index.html +++ b/archives/2019/12/index.html @@ -163,7 +163,7 @@ isHome: false, isHighlightShrink: false, isToc: false, - postUpdate: '2025-01-07 10:39:35' + postUpdate: '2025-03-17 21:25:37' }
全部文章 - 1
2019
年终总结 - 2019
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me
最新文章
+
全部文章 - 1
2019
年终总结 - 2019
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me
最新文章
@@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 1
    2019
    年终总结 - 2019
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 1
    2019
    年终总结 - 2019
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 1
    2021
    年终总结 - 2020
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 1
    2021
    年终总结 - 2020
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 20
    2022
    年终总结 - 2021
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 21
    2022
    年终总结 - 2021
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 1
    2022
    年终总结 - 2021
    2021
    年终总结 - 2020
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 1
    2022
    年终总结 - 2021
    2021
    年终总结 - 2020
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 1
    2022
    操统实验日志 第四章 勇者之路
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 1
    2022
    操统实验日志 第四章 勇者之路
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 20
    2023
    年终总结 - 2022
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 21
    2023
    年终总结 - 2022
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 20
    2025
    年终总结 - 2023 至 2024
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 21
    2025
    年终总结 - 2023 至 2024
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 1
    2025
    Coroutine 相关疑惑大模型问答记录
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    \ No newline at end of file diff --git a/archives/2025/index.html b/archives/2025/index.html new file mode 100644 index 0000000..94e6e38 --- /dev/null +++ b/archives/2025/index.html @@ -0,0 +1,347 @@ +2025 | 時痕 + + + + + + + + + + +
    全部文章 - 2
    2025
    Coroutine 相关疑惑大模型问答记录
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    \ No newline at end of file diff --git a/archives/index.html b/archives/index.html index d3b44ae..2f05d2f 100644 --- a/archives/index.html +++ b/archives/index.html @@ -163,7 +163,7 @@ isHome: false, isHighlightShrink: false, isToc: false, - postUpdate: '2025-01-07 10:39:35' + postUpdate: '2025-03-17 21:25:37' }
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 21
    2019
    年终总结 - 2019
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    \ No newline at end of file diff --git a/categories/index.html b/categories/index.html index 3946e4c..0b106ed 100644 --- a/categories/index.html +++ b/categories/index.html @@ -7,7 +7,7 @@ - + @@ -165,7 +165,7 @@ isHome: false, isHighlightShrink: false, isToc: false, - postUpdate: '2025-01-07 10:39:22' + postUpdate: '2025-03-17 21:25:21' }
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -192,7 +192,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -230,7 +230,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    分类 - 技术
    2022
    操统实验日志 第一章 序章
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2025 - 1 + 2
  • @@ -228,7 +228,7 @@ btf.addGlobalFn('pjaxSend', () => { 1 -
    网站信息
    文章数目 :
    20
    本站总字数 :
    80.8k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :