diff --git a/2022/07/15/os-journal-vol-1/index.html b/2022/07/15/os-journal-vol-1/index.html index 85113ba..9c2e643 100644 --- a/2022/07/15/os-journal-vol-1/index.html +++ b/2022/07/15/os-journal-vol-1/index.html @@ -7,7 +7,7 @@ - + @@ -166,7 +166,7 @@ isHome: false, isHighlightShrink: false, isToc: true, - postUpdate: '2024-10-15 13:17:40' + postUpdate: '2024-10-20 23:46:30' }

操统实验日志 第一章 序章

简述

+

操统实验日志 第一章 序章

简述

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

它是关于什么的

@@ -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 许可协议。转载请注明来源 時痕
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me
最新文章

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

关于本章

+

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

关于本章

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

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

在本章的后半部分,将会介绍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文件系统接口的设计和实现。

@@ -1836,13 +1836,16 @@ VirtualPageIndex &= VirtualPageAddress >> n \nonumber \\

其中的成员函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PageTable* PageTable::from(const PageTableEntry& entry) {
return (PageTable*)entry.address();
}

PageTable* PageTable::from(const uint32 addr) {
return (PageTable*)addr;
}

PageTableEntry& PageTable::at(uint32 idx) {
return _entries[idx];
}

void PageTable::init() {
for(int i = 0; i < 1024; i++) {
_entries[i] = PageTableEntry();
}
}
+ + +

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

-
文章作者: Linloir
文章链接: https://blog.linloir.cn/2022/08/20/os-journal-vol-4/
版权声明: 本博客所有文章除特别声明外,均采用 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. +
  3. 根据 在 NAS 上部署自己的 Gitea 服务,无需公网服务器 方案打通外网到家用 NAS / MacMini 的链路
  4. 采用 Git 仓库 main 分支存放源码 + Gitea Actions 编译至 publish 分支实现源码及制品存储
  5. 使用 caddy-git 插件实现拉取 Git 仓库 publish 分支并作为 fileserver 由 Caddy 反向代理
@@ -185,12 +184,10 @@ btf.addGlobalFn('pjaxSend', () => {

其中红色线条为 HTTP 流量,蓝色线条为 DDNS-GO 流量,紫色线条为本地或 v6 直连的 ssh TCP 流量

blog_topology


-

环境准备

-

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

+

环境准备

在配博客之前,我是先配好了 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. +
  3. 公网访问使用光猫桥接路由器拨号,通过路由器同时获取 IPv4 大内网和 IPv6 公网 /64 地址
  4. DNS 解析通过 MacMini 上部署的 ddns-go 实现
  5. -
  6. v6 / v4 双栈访问采用了 Cloudflare 中针对 DNS 解析记录的 Proxied 能力
  7. -
  8. 80 / 443 端口封禁通过在 MacMini 上开放 Cloudflare Proxy 支持的端口 并在 Cloudflare 控制台指定回源端口解决
  9. -
  10. Gitea 仓库直接在 Nas 上通过 Docker Compose 部署,通过 MacMini 上 Caddy 反向代理 http/https 流量
  11. +
  12. v6 / v4 双栈访问采用了 Cloudflare 中针对 DNS 解析记录的 Proxied 能力
  13. +
  14. 80 / 443 端口封禁通过在 MacMini 上开放 Cloudflare Proxy 支持的端口 并在 Cloudflare 控制台指定回源端口解决
  15. +
  16. Gitea 仓库直接在 Nas 上通过 Docker Compose 部署,通过 MacMini 上 Caddy 反向代理 http/https 流量
  17. TLS 采用 caddy-dns 插件,通过 Cloudflare token 实现 dns 验证,并由 Caddy 自动部署证书

-

背景

-

大四毕业上班之后,便不再和父母一起住了,自然而然地就想改善一下家里的网络环境,也正好实践一下刚考完研还热乎的计算机网络知识,而不至于像之前重启一下路由器马上就能听到 “怎么没网了” 这样的投诉。

-

做的第一件事就是选一个宽带运营商。由于我自己两个号码都是中国联通的,一张是米粉卡 5r / mo 月租 + 1r / GiB (3r 不限量) 日租流量包的套餐,另一张曾经也是米粉卡,后来手贱改成了 19r / mo 月租 + 3GiB 流量 + 1r / GiB 日租流量包的大王卡套餐,一直都觉得第二张卡套餐太亏,于是想趁着宽带的机会把这张卡和宽带绑在一起共用一个套餐 (事实证明其实没什么用,因为现在宽带都改成最低消费模式了,也就是只要满足卡的每月最低消费就能有宽带,如果没有达到最低消费就直接扣中间的差额。所以其实这么看还是米粉卡最划算,相当于每月不限量流量才 95r,宽带 139r 低消只用再补 40r,所以有便宜套餐千万不要随便改啊),就选了联通 139r / mo 的 1000M 宽带的套餐。

+

背景

大四毕业上班之后,便不再和父母一起住了,自然而然地就想改善一下家里的网络环境,也正好实践一下刚考完研还热乎的计算机网络知识,而不至于像之前重启一下路由器马上就能听到 “怎么没网了” 这样的投诉。

+

做的第一件事就是选一个宽带运营商。由于我自己两个号码都是中国联通的,一张是米粉卡 5r / mo 月租 + 1r / GiB (3r 不限量) 日租流量包的套餐,另一张曾经也是米粉卡,后来手贱改成了 19r / mo 月租 + 3GiB 流量 + 1r / GiB 日租流量包的大王卡套餐,一直都觉得第二张卡套餐太亏,于是想趁着宽带的机会把这张卡和宽带绑在一起共用一个套餐 (事实证明其实没什么用,因为现在宽带都改成最低消费模式了,也就是只要满足卡的每月最低消费就能有宽带,如果没有达到最低消费就直接扣中间的差额。所以其实这么看还是米粉卡最划算,相当于每月不限量流量才 95r,宽带 139r 低消只用再补 40r,所以有便宜套餐千万不要随便改啊),就选了联通 139r / mo 的 1000M 宽带的套餐。

办套餐的时候最关心的就是:

  1. 有没有 IPv4 公网地址
  2. @@ -196,49 +194,45 @@ btf.addGlobalFn('pjaxSend', () => {
  3. 下行实际带宽能有多少

在办的时候专门确认了前三点,确认是不会有公网 IPv4 但是有公网 IPv6,上行带宽差不多 150M,想了想感觉也够了,遂同意,签合同,上门安装,便也有了后文。

-

光猫桥接和路由器拨号

-

这个 139r 的联通套餐实际上是他们的 FTTR 解决方案,会带一个主路由和一个副路由,中间用隐形光纤连接,主路由给的是 2.5G 网口版本,这点好评,不过官方的配网方案感觉应该是 AC + AP 模式,光猫必须作为网关,然后路由器估计也得用 AP 模式不然感觉应该路由器连接的设备和官方的 AP 连接的设备就不在一个子网了。

+

光猫桥接和路由器拨号

这个 139r 的联通套餐实际上是他们的 FTTR 解决方案,会带一个主路由和一个副路由,中间用隐形光纤连接,主路由给的是 2.5G 网口版本,这点好评,不过官方的配网方案感觉应该是 AC + AP 模式,光猫必须作为网关,然后路由器估计也得用 AP 模式不然感觉应该路由器连接的设备和官方的 AP 连接的设备就不在一个子网了。

当即就觉得这样不行,还是得该桥接,代价就是官方的 AP 就浪费了 (虽然我感觉好像是不是如果把 AP 连上后面交换机的光口应该也能用?不知道官方的 AP 里面是什么逻辑),但反正也是不要钱的东西,浪费就浪费了,于是初步规划了一下家里的网络拓扑如下:

network_topology

-

啥是光猫桥接

-

以我浅薄的计算机网络知识理解一下光猫桥接:

-

在默认情况下,光猫作为家里的网关路由,光猫出口端连接的是运营商网络,具有运营商分配的 IP;光猫另一端连接家庭网络的其他设备,运行在 192.168.1.0/24 子网 A (随便命一个名字方便区分) 下,并且光猫使用固定 IP 192.168.1.1 接入这个子网。

-

之后,如果路由器选择路由模式,则路由器与光猫连接的一端接入子网 A 中,并具有光猫分配的 IP,此时所有设备和路由器连接,并以路由器作为它们的网关,此时,这些设备和路由器向内的端口同处一个子网 B 中,一般也是 192.168.1.0/24 这个网段,路由器以 IP 192.168.1.1 接入这个子网。

+

啥是光猫桥接

以我浅薄的计算机网络知识理解一下光猫桥接:

+

在默认情况下,光猫作为家里的网关路由,光猫出口端连接的是运营商网络,具有运营商分配的 IP;光猫另一端连接家庭网络的其他设备,运行在 192.168.1.0/24 子网 A (随便命一个名字方便区分) 下,并且光猫使用固定 IP 192.168.1.1 接入这个子网。

+

之后,如果路由器选择路由模式,则路由器与光猫连接的一端接入子网 A 中,并具有光猫分配的 IP,此时所有设备和路由器连接,并以路由器作为它们的网关,此时,这些设备和路由器向内的端口同处一个子网 B 中,一般也是 192.168.1.0/24 这个网段,路由器以 IP 192.168.1.1 接入这个子网。

相反,如果路由器选择 AP 模式,则路由器可以理解为一个无线交换机,其不分割子网,所有设备和光猫内侧端口处于同一个子网 A 中,此时使用 IP 192.168.1.1 则可以访问到光猫管理页面。

如果将光猫桥接,则可以理解为,光猫以一种子网设备的形式连接到运营商和家里真实网关设备之间的子网上,也就是可以想象成如下的网络拓扑结构:

bridge_topology

也就是,光猫自己充当这个交换机的角色,并以一个固定 IP 192.168.1.1 接入到广播域 A 中。路由器和入户网线都与光猫充当的交换机连接,路由器实际与运营商建立拨号连接并获得运营商提供的 IP,充当广播域 B 的网关设备。

为什么要强调光猫以固定 IP 接入广播域 A 中呢,因为从拓扑图中也可以看到,一旦光猫开启了桥接模式,并且所有的设备都与路由器连接,在广播域 B 中的设备将无法访问到广播域 A 中的光猫,想要访问光猫只有在路由器中建立路由表转发 192.168.1.1 的包到广播域 A 中或是直接将设备接入广播域 A 中才能通过 192.168.1.1 访问到光猫。

-

超管密码

-

其实改光猫桥接很简单,网上针对不同的光猫型号和运营商都有很丰富的教程了,反倒是超管密码的获取才是最难的一步,这就要考验和安装人员拉扯的话术了。

+

超管密码

其实改光猫桥接很简单,网上针对不同的光猫型号和运营商都有很丰富的教程了,反倒是超管密码的获取才是最难的一步,这就要考验和安装人员拉扯的话术了。

还记得当时和安装小哥说能不能改成桥接,小哥说现在公司规定他们是不可以这么操作的,也就是说以前明着要求小哥改桥接的路子就不太行得通了。后来想了一下,问小哥之后要是网络出了什么问题是不是要找他上门帮我弄,小哥说是的,我又问了价格,说这种不收钱的,我就顺水推舟说要不给我个超管的密码,我自己也会配网络,如果有什么问题我自己弄一下就好了也不麻烦他上门一趟,还没有费用拿,小哥很高兴就给我写了个小纸条留了密码,还嘱咐说这个密码是动态的,过几个月就失效了,要配得快点,失效了可以再问他要。

果然沟通还是要拿捏住让双方都获利还不明显违规的点哈哈,给我密码对他来说节省了跑一趟的时间和精力,减少了投诉率,对我来说又能够达成目的,还不违背运营商禁止小哥配桥接的要求,问就是我自己淘宝买的超管密码。

-

拨号上网和 IPv6

-

光猫桥接搞定以后,就是把路由器连接上光猫然后配置拨号连接了。

+

拨号上网和 IPv6

光猫桥接搞定以后,就是把路由器连接上光猫然后配置拨号连接了。

拨号的账号可以在光猫的页面看到,密码则可以问运营商人工客服,一般默认就是账号的后 6 位,IPv6 可以复用 IPv4 线路进行拨号,如下图所示:

ipv6_auth

由于需要允许内网设备与公网建立连接,需要关闭 IPv6 防火墙功能,其他厂商是否需要关闭取决于厂商如何定义它们的防火墙。TP-Link 对 IPv6 防火墙的描述为:“IPv6 防火墙能有效将外网和内网进行隔离,避免家庭局域网完全暴露给外网,保障用户的网络安全。”

同时,地址获取协议和前缀授权就都选择自动,DNS 服务器我选择了 Cloudflare 的 1.1.1.1 中的配置。

ipv6_config

-

针对局域网内的设备,可以根据运营商分配的数量灵活选择 SLAAC 和 DHCPv6。由于抠门的深圳联通只给了 /64 地址,相当于只有直接连接到路由器的设备可以使用 SLAAC 分配地址,下级设备无法继续使用 SLAAC 分配,索性我就直接关闭了 SLAAC 能力改用 DHCPv6。

+

针对局域网内的设备,可以根据运营商分配的数量灵活选择 SLAAC 和 DHCPv6。由于抠门的深圳联通只给了 /64 地址,相当于只有直接连接到路由器的设备可以使用 SLAAC 分配地址,下级设备无法继续使用 SLAAC 分配,索性我就直接关闭了 SLAAC 能力改用 DHCPv6。

ipv6_config_2

-

DDNS 动态域名解析与 Cloudflare Proxy

-

路由有了 IPv6 之后,接上机柜里的 MacMini,就可以使用 IPv6 访问到这台机器了,先拿个 python http 服务试试看通不通:

+

DDNS 动态域名解析与 Cloudflare Proxy

路由有了 IPv6 之后,接上机柜里的 MacMini,就可以使用 IPv6 访问到这台机器了,先拿个 python http 服务试试看通不通:

1
2
cd ~/some_dir
python -m http.server 23333
-

IPv6 地址可以使用 ipconfig (Windows) 或是 ip addr (Linux/Mac) 命令查看到,一般是查看 无线局域网适配器 WLAN 或是 以太网适配器 (Windows) 或是 eth0 (Linux),en0 (Mac) 下面的 IPv6 或是 inet6 标识的地址。还会有很多标有 临时 或是 deprecated 的地址,那些是系统为了防止网站跟踪 IP 地址,轮替生成的用于主动建立连接时使用的地址,这些地址有效期较短并且时常变化,不适用于监听传入的连接。

+ +

IPv6 地址可以使用 ipconfig (Windows) 或是 ip addr (Linux/Mac) 命令查看到,一般是查看 无线局域网适配器 WLAN 或是 以太网适配器 (Windows) 或是 eth0 (Linux),en0 (Mac) 下面的 IPv6 或是 inet6 标识的地址。还会有很多标有 临时 或是 deprecated 的地址,那些是系统为了防止网站跟踪 IP 地址,轮替生成的用于主动建立连接时使用的地址,这些地址有效期较短并且时常变化,不适用于监听传入的连接。

inet6_addr

浏览器直接 http://[这里填 IPv6 地址]:端口号 即可测试能不能正常访问了,不放心可以流量访问确定链路是通的。

接下来就该把这个地址解析成域名了。因为 IPv6 始终不是静态的,因此需要动态地轮询设备的 IPv6 地址然后更新 DNS 服务商上的解析内容。

-

ddns-go 安装

-

参考 ddns-go 文档即可快速在系统中安装 ddns-go。没有选择 Docker 主要是看到可以使用 brew 安装,甚至感觉比 docker 还简单。

+

ddns-go 安装

参考 ddns-go 文档即可快速在系统中安装 ddns-go。没有选择 Docker 主要是看到可以使用 brew 安装,甚至感觉比 docker 还简单。

1
2
brew install ddns-go
ddns-go -s install -l 23333
-

这样就启动了控制面板在 23333 端口的 ddns-go 服务,配置文件默认保存在 ~/.ddns_go_config.yaml 中,这个我个人懒得改,想改看文档可以 -c 指定路径

+ +

这样就启动了控制面板在 23333 端口的 ddns-go 服务,配置文件默认保存在 ~/.ddns_go_config.yaml 中,这个我个人懒得改,想改看文档可以 -c 指定路径

然后就是 http://192.168.X.X:XXXXX 登陆进去面板配置了,这里需要注意的是需要局域网登录进去配置,如果是非局域网地址会拒绝连接。

Webhook 通知我选择了 Server 酱,每天免费 5 条消息也够用,Webhook 格式配置如下:

1
https://sctapi.ftqq.com/<my_token>.send?title=检测到 DDNS 设备 IP 变化&desp=MacMini の IP 已变更为 #{ipv6Addr}, 域名更新 #{ipv6Result}
+

搞完以后就可以浏览器进 域名:端口号 看看服务生没生效啦,在这一步测试的时候,记得在 Cloudflare 中关闭 Proxied 模式,后面会具体讲到原因。

-

Cloudflare Proxied 代理

-

这个时候已经可以在 IPv6 的网络下通过 域名:端口号 访问到服务了,但是这时候还有两个令人头疼的问题:

+

Cloudflare Proxied 代理

这个时候已经可以在 IPv6 的网络下通过 域名:端口号 访问到服务了,但是这时候还有两个令人头疼的问题:

  1. 很多时候网络环境是仅 IPv4 的,例如各种酒店、公用 WiFi、公司内网等等,仅 IPv6 的网络访问覆盖率还是太低,感觉实用性不强
  2. 网址后面带着端口号,怎么看都很丑,有些不能指定端口号的地方就会收受到阻碍
  3. @@ -251,11 +245,10 @@ btf.addGlobalFn('pjaxSend', () => {
  4. 回源的时候支持指定具体的端口号,这样就可以在网址里不再输入端口号了

开启 Proxy 十分简单,直接在 DNS 记录里打开 Proxied 开关即可。

-

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

+

不过,由于 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 代码的能力。

+

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

+
    +
  1. .dex 文件方式:
      +
    1. 创建一个普通的 Java 工程,正常地添加依赖,这里需要正确配置确保依赖在构建的时候也会被打包进制品 jar 中,编写相关代码
    2. +
    3. 构建 jar 包
    4. +
    5. 在安卓 SDK 文件夹下 (一般为 <homedir>\AppData\Local\Android\Sdk),找到 cmdline-tools\latest\bin\d8.bat (这里可以将 <homedir>\AppData\Local\Android\Sdkcmdline-tools\latest\bin 添加到环境变量中方便后面调用 d8 命令),如果没有,可以在 Android Studio 更新 Commandline Tools 或是在 build-tools\<version> 下找到一个能用的
    6. +
    7. 使用 d8 [options] <source jar> 命令编译出 .dex 文件,例如 d8 server.jar,会在当前工作目录下生成 classes.dex
    8. +
    +
  2. +
  3. .jar 文件方式:
      +
    1. 创建一个 Empty Activity 的 Android Studio 安卓工程,正常编写 Java 代码 (在 app/src/main/java 下) 以及添加依赖
    2. +
    3. 使用 Android Studio 的构建 apk 能力打包一个 apk (例如 app-debug.apk)
    4. +
    5. .apk 后缀改为 .jar
    6. +
    +
  4. +
+

得到 .jar 或是 .dex 文件以后,使用 adb [-s serial] push <source file> <target> 命令推送到手机端

+

最后,在 shell 环境执行 app_process call 起 Java 程序:

+

app_process 启动命令为 app_process -Djava.class.path=<path to jar> [other java options] <working directory (preferred /)> com.your.package.Class [args] ...,也即对于打包好的 server.jar 文件,执行命令可能为 adb shell app_process -Djava.class.path=/data/local/tmp/server.jar / com.linloir.server.Main --port 23456

+
+

背景

起因是想要做一个检查安卓设备网络连通性的能力,最初想的就是 adb ping 直接秒了,甚至还可以复杂点用 adb ip route get 1.1.1.1 拿到网关以后分别去 ping 网关和外网地址,从而判断设备到网关的连通性和网关到外网的连通性

+

但是很快遇到了一个很诡异的现象:某些设备连续 ping 同一个地址 (即 A 先 ping 网关,然后 B ping 网关,最后 C ping 网关,串行执行) 时,会有一些设备出现 80% 以上的丢包甚至直接完全无法 ping 通的情况,并且当某台设备 ping 不通的同时,再开一个 shell 让它 ping 另外一个地址 (例如外网地址),又是完全正常的,并且同一时间之前卡住的还是完全卡住的状态。反复研究发现有那么一些手机互相就像有羁绊一样,只要挨个 ping 相同地址就基本后一台铁挂,由于时间有限同时能力也有限,暂时没有深究了 (如果有后续就再补一篇文章)

+

于是,就因为这个 ping 这样谎报军情的现象,导致最后实在没办法再采用原本的方案,被迫不得不去找一个新的、能够兼容所有主流安卓设备的方案

+

思路很快确定了,就是做一个 HTTP 服务,给个 /ping 接口,客户端这边去发 GET 请求然后确认回包 200 即可。但是,问题就在于,如何能够在所有主流安卓设备上发 HTTP 请求然后判断结果呢?

+

最初,我想到了 curl,然后发现不是所有的设备都有 curl;于是转而投奔 nc,结果发现 nc 也不是所有的设备都有;其他的命令其实基本就也不想再试了,因为就算当前手头上的设备都能支持,谁也说不好会不会新来的机器就不支持了…

+

这时候都感觉仿佛只能用最最丑陋的下策了 —— 装一个 apk 包然后用 adb 拉起来然后走 socket 通信去执行 HTTP 请求。但是这样的方案,在自动化工程里面几乎是不可接受的,比如,光是自动化安装包这个环节就有各种坑可以踩,就算手动装也有不小的工作量;同时,call 起 apk 的过程也可能影响前台的 app。总之,这个方案一定是利大于弊的,因此也就没有第一时间去做尝试,而是继续去寻找可行的方案

+

经过同事点拨,突然想到,平常自动化测试用到的 openatx/uiautomator 这个包,它本质上就是个 python 包装的反向代理,使得电脑端通过 python API 和手机侧的 server 通信,再把请求转换成 uiautomator 的指令加以执行。那么既然 openatx/uiautomator 是这样的 server-client 架构,那它肯定在手机上也跑了个 server 服务吧,这个服务之前有注意到就是一个 u2.jar 文件,所以应该意味着,我应该也可以写一个 Java 程序然后想办法在安卓端跑起来

+

然后就开始了后文中试图让 Java 能在 adb shell 中跑起来的各种尝试…

+

尝试

通过 dalvikvm 执行 .dex 文件

最初的思路比较直接,安卓可以说是基于 Java 的,早期的运行时虚拟机 dalvikvm 即便到了 ART 时代仍然被系统所兼容,并且 dalvikvm 命令也仍然存在于所有安卓设备上,于是便试图通过 dalvikvm -cp <dex file> com.your.package.Class 来执行代码

+

在 demo 验证阶段,一个简单的 Hello World 程序是可以运行的,并且所有的设备都能够正确的执行,这基本证明了两点:

+
    +
  1. 使用 Java 来编写兼容大部分主流安卓设备的特殊逻辑代码并在设备上执行这一思路是可行的
  2. +
  3. dalvikvm 确实可以执行 Java 程序
  4. +
+

但是很快,在 HTTP 请求的代码验证中,部分设备就出现了各种各样的错误:

+
    +
  1. 触发 Segmentation fault
  2. +
  3. 触发异常 java.lang.UnsatisfiedLinkError 并且直接无法执行
  4. +
  5. 触发 java.lang.ClassNotFoundException 异常但是不影响执行
  6. +
+

其中,第二种异常提供了一些有用的信息:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.okhttp.OkUrlFactory.startBoost(OkUrlFactory.java:127)
at com.android.okhttp.OkUrlFactory.open(OkUrlFactory.java:79)
at com.android.okhttp.OkUrlFactory.open(OkUrlFactory.java:69)
at com.android.okhttp.HttpHandler.openConnection(HttpHandler.java:44)
at java.net.URL.openConnection(URL.java:993)
at HelloWorld.sendGetRequest(HelloWorld.java:64)
at HelloWorld.main(HelloWorld.java:26)
Caused by: java.lang.UnsatisfiedLinkError: No implementation found for android.os.IBinder com.android.internal.os.BinderInternal.getContextObject() (tried Java_com_android_internal_os_BinderInternal_getContextObject and Java_com_android_internal_os_BinderInternal_getContextObject__)
at com.android.internal.os.BinderInternal.getContextObject(Native Method)
at android.os.ServiceManager.getIServiceManager(ServiceManager.java:40)
at android.os.ServiceManager.getService(ServiceManager.java:56)
at com.oppo.luckymoney.LMManager.init(LMManager.java:209)
at com.oppo.luckymoney.LMManager.<init>(LMManager.java:197)
at com.oppo.luckymoney.LMManager.getLMManager(LMManager.java:202)
... 8 more
+ +

它指出,即便我在上层使用的是 java.net.URL.openConnection,下层应用的实现还是变成了 com.android.okhttp.OkUrlFactory 这种设备相关的代码,这应该就是导致在不同设备上会有不通执行结果的原因

+

进一步猜测,UnsatisfiedLinkError 指出部分动态链接库没有被正确地加载,考虑到对于一个普通程序而言,在启动时安卓运行时肯定会进行一些初始化操作,其中肯定有一部分链接库会在这个阶段由运行时去完成链接,而 dalvikvm 作为直接与虚拟机交互的指令,很有可能就是缺少了这样一些准备操作导致一些相关的运行时库没有被正确链接上,进而导致了 UnsatisfiedLinkError 甚至是 Segmentation Fault

+

依据这样的猜测,dalvikvm 应当是行不通了,势必要找到一个与安卓运行时挂钩的东西去执行 .dex 才能够避免同样的问题

+

通过 uiautomator 1.0 执行 .jar 文件

目光再次转向 atx/uiautomator 工程,在 readme 中读到了 uiautomator 1.0 时代能够通过 uiautomator <jar> ... [-c class] 的方式来执行 .jar 文件中自动化测试的代码,遂猜测其也是使用这样的方式启动的 u2.jar 文件。(其实这里犯了一个很大的错误让我与真相擦肩而过,进而浪费了许多时间在这一步上,这一点将在 经验与教训 一节详述)

+

网上能找到的文档大致描绘了一个这样的步骤:

+
    +
  1. 创建一个 Java 项目
  2. +
  3. 添加 android.jaruiautomator.jar 依赖
  4. +
  5. 使用 android create uitest-project -n <name> -t <target version that match android.jar and uiautomator.jar> -p <path to project> 创建 xml
  6. +
  7. 使用 ant 构建 jar 包
  8. +
  9. 使用 uiautomator <jar> ... [-c class] 命令启动
  10. +
+

很遗憾的是,当前 Android SDK 中的 android 命令早已不包含 create uitest-project 这一条指令了,并且官方早就删除了与 uiautomator 1.0 相关的文档。同时,现有的各种打包文档也都含糊不清,IDE 也还是 Eclipse,想要在当前的环境成功打包出来可以说是难上加难了。我尝试下载了旧版的 SDK、ant 以及 Java JDK8 来尝试完成这个工作,总是在各种环节出现不兼容的问题,遂最终也作罢。事后复盘想想估计就算真的成功编译了,估计是否兼容如今的安卓版本也是个大问题,并且要是别人想要复现一次这个过程也要遭受这样的苦难,实在不是一个值得尝试的思路

+

最终方案

最终,是在一篇关于 scrcpy 源码解读 的博客中找到了一段关键的内容:

+
+

上面我们提到 scrcpy-server.jar 是通过 app_process 运行起来的,那么 app_process 又是什么鬼?app_process 是启动 zygote 和其他 Java 程序的应用程序,它可以让虚拟机从 main() 方法开始执行一个 Java 程序。具体的用法可以参考 app_process 源码中的注释:

+
1
"Usage: app_process [java-options] cmd-dir start-class-name [options]\n"
+ +

与 APP 进程不同,通过 app_process 启动的进程可以在 root 权限和 shell 权限 (adb 默认) 下启动,也就分别拥有了调用不同 API 的能力。通常情况下 shell 权限启动的 app_process 只能够调用一些能够完成 adb 本身工作的 API,root 权限启动的 app_process 进程则拥有更多权限,甚至能够调用系统 signature 保护级别的 API 及访问整个文件系统。

+

实际上不少 adb 命令都是对调用 app_process 进行了一些封装,这里举我们平时常用的 am 指令为例,我们在执行 adb shell am 的时候其实是执行了以下的脚本。通过 app_process 运行 am.jarcom.android.commands.am.Am 这个类的 main() 函数来完成具体操作。

+
1
2
3
4
5
6
7
8
#!/system/bin/sh
if [ "$1" != "instrument" ] ; then
cmd activity "$@"
else
base=/system
export CLASSPATH=$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "$@"
fi
+ +

这里需要注意的是 app_process 只能运行原始 dex 文件,也可以接收包含 classes.dexjar 包,如 am.jar。这里 scrcpy 取巧直接用了编译 apk 的方式,再直接重命名为 jar 包,这样也可以被 app_process 运行。而且可以使用 gradle 方便的进行编译,直接在 Android Studio 中开发,调用原生 API,像源码中的 IRotationWatcher.aidl 也可以直接生成相应的 IPC Proxy 类进行调用,省力又省心。

+

scrcpy-server.jaradb 下通过 app_process 运行起来后,默认就有了 shell 权限,像一些截屏、录屏、模拟按键点击等功能都可以直接使用,而且完全不需要任何权限的声明。

+
+

也就是说,app_process 就是我最开始想要找的,带完整运行时版本的 dalvikvm

+

在引用的文章中,也提到了 scrcpy 使用的一种打包方法:

+
    +
  1. 创建一个 Empty Activity 的 Android Studio 安卓工程,正常编写 Java 代码 (在 app/src/main/java 下) 以及添加依赖
  2. +
  3. 使用 Android Studio 的构建 apk 能力打包一个 apk (例如 app-debug.apk)
  4. +
  5. .apk 后缀改为 .jar
  6. +
+

进一步分析,其实本质上就是借用了 Android Studio 将 Java 代码打包为 dex 文件的能力,真正使用的还是 apk 文件中的 classes*.dex 文件,通过以 .zip 打开构建的 apk 并删除其他无关文件再测试可以证实这一点。而由于 dex 本质上还是在给 Java 解释器提供 classpath,只是可能不同于 jar 文件的组织方式,因此理论上对于其他方式打包的 dex 文件,app_process 应该也是支持的

+

这里想到前面用到的 d8.bat 工具,其就是将 .jar 文件转换为 .dex 文件,遂进行测试,证实了另一种打包的思路:

+
    +
  1. 创建一个普通的 Java 工程,正常地添加依赖,这里需要正确配置确保依赖在构建的时候也会被打包进制品 jar 中,编写相关代码
  2. +
  3. 构建 jar 包
  4. +
  5. 在安卓 SDK 文件夹下 (一般为 <homedir>\AppData\Local\Android\Sdk),找到 cmdline-tools\latest\bin\d8.bat (这里可以将 <homedir>\AppData\Local\Android\Sdkcmdline-tools\latest\bin 添加到环境变量中方便后面调用 d8 命令),如果没有,可以在 Android Studio 更新 Commandline Tools 或是在 build-tools\<version> 下找到一个能用的
  6. +
  7. 使用 d8 [options] <source jar> 命令编译出 .dex 文件,例如 d8 server.jar,会在当前工作目录下生成 classes.dex
  8. +
+

经过测试,其中第 4 点是必须的,原因在引用的文章中也有提到:这里需要注意的是 app_process 只能运行原始 dex 文件,也可以接收包含 classes.dexjar,也就是说对于 app_process.jar 可能只是被作为一个文件夹看待而不是作为实际的 classpath 看待,实际作为 classpath 的是提供的 .dex 或是 .jar 中的 .dex

+

由此可以看出,理论上只要能打包出带完整依赖的 jar 包,辅以 d8 的转换,就可以用来作为 app_process 的 classpath 了,只是使用 Android Studio 方案的明显优势就是对于 Android API 的天然支持和对于依赖管理和导出的便捷性吧,这里可以根据具体的需求和场景来选择

+

在有了 .jar 或是 .dex 后,参考 app_process 的启动参数:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app_process [java-options] cmd-dir [--application | --zygote [--start-system-server]] start-class-name [options]

app_process executes a interpreted runtime environment for dalvik bytecodes in an application-alike environment

if together with --application option, System.out is redirected to logcat.
otherwise, System.out remains untouched, i.e., the output is the stdout.

app_process becomes zygote if starts with --zygote,

if together with --start-system-server option, zygote also starts system server by forking itself.
app_process can be used as a general program besides starting zygote.

java-options: Options of dalvik/libart
cmd-dir: Root path of the running process (e.g., root path of file operations)
start-class-name: Class to run
options: Options of the running class
+ +

即对于打包好的 server.jar 文件,执行命令可能为 adb shell app_process -Djava.class.path=/data/local/tmp/server.jar / com.linloir.server.Main --port 23456

+

经验与教训

    +
  1. 在实现需要部署在多设备上的需求时,优先考虑平台或设备无关的实现方案,例如跨平台语言或是设备统一的解决方案 (如 apk)
  2. +
  3. 验证不一定要在原先的工程里摘代码验证,写 bash 也可以,例如 curl 的验证应当直接写 bash 脚本去在每个 adb devices 获取到的设备上验证,而不是频繁地修改主体工程里与这一块逻辑相关的 go 代码再通过单测函数验证,这样明显会浪费更多时间,bash 脚本 GPT 很快就能实现然后进行快速验证
  4. +
  5. 在研究 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 的打包方式上浪费了大半天的时间
  6. +
  7. 对于一个陌生的仓库,如果想要了解它的源码实现,不一定非要自己去读,除了进行仓库里的关键词搜索,也可以去查找别人的源码解析,走个捷径
  8. +
+
文章作者: Linloir
文章链接: https://blog.linloir.cn/2024/10/20/jar-via-adb/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 時痕
\ No newline at end of file diff --git a/archives/2022/07/index.html b/archives/2022/07/index.html index 3045114..d577f43 100644 --- a/archives/2022/07/index.html +++ b/archives/2022/07/index.html @@ -163,7 +163,7 @@ isHome: false, isHighlightShrink: false, isToc: false, - postUpdate: '2024-10-15 13:17:49' + postUpdate: '2024-10-20 23:46:42' }
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me
最新文章
+
avatar
Linloir
我、技术、生活与值得分享的一切
Follow Me
最新文章
@@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    全部文章 - 1
    2022
    操统实验日志 第四章 勇者之路
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 1
    2022
    操统实验日志 第四章 勇者之路
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    全部文章 - 11
    2022
    操统实验日志 第一章 序章
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    + + 分类 + +
    +
    +
    + + 归档 + +
    +
    网站信息
    文章数目 :
    11
    本站总字数 :
    69.4k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    \ No newline at end of file diff --git a/categories/index.html b/categories/index.html index 85bf9f0..6267e09 100644 --- a/categories/index.html +++ b/categories/index.html @@ -7,7 +7,7 @@ - + @@ -165,7 +165,7 @@ isHome: false, isHighlightShrink: false, isToc: false, - postUpdate: '2024-10-15 13:17:40' + postUpdate: '2024-10-20 23:46:30' }
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -192,7 +192,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -202,7 +202,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    分类 - 杂思
    2024
    短文 - 关于短博文的碎碎念
    重生
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    分类 - 杂思
    2024
    短文 - 关于短博文的碎碎念
    重生
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    分类 - 生活
    2024
    短文 - 转正日
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    分类 - 生活
    2024
    短文 - 转正日
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    +
    avatar
    Linloir
    我、技术、生活与值得分享的一切
    Follow Me
    最新文章
    @@ -190,7 +190,7 @@ btf.addGlobalFn('pjaxSend', () => {
  • 2024 - 6 + 7
  • @@ -200,7 +200,7 @@ btf.addGlobalFn('pjaxSend', () => { 4 -
    网站信息
    文章数目 :
    10
    本站总字数 :
    65.3k
    本站访客数 :
    本站总浏览量 :
    最后更新时间 :