<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Hexo</title>
  
  
  <link href="https://blog.haozi-haozi.cn/atom.xml" rel="self"/>
  
  <link href="https://blog.haozi-haozi.cn/"/>
  <updated>2026-03-24T08:57:07.047Z</updated>
  <id>https://blog.haozi-haozi.cn/</id>
  
  <author>
    <name>John Doe</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>基于 QT-Android 的计划/健身/学习类软件--MousePlan</title>
    <link href="https://blog.haozi-haozi.cn/2026/03/20/Qt-MousePlan-Android/"/>
    <id>https://blog.haozi-haozi.cn/2026/03/20/Qt-MousePlan-Android/</id>
    <published>2026-03-20T11:34:56.000Z</published>
    <updated>2026-03-24T08:57:07.047Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-引入"><a href="#A-引入" class="headerlink" title="A 引入"></a>A 引入</h2><p>因为学院不让实习来着，<del>所以这学期办了一张健身卡</del>，锻炼身体还能调整作息，一直想找一个非常适合查看每天该练什么，多少次，多少重量，还能打卡记录的相关软件，但是感觉都不是那么契合，要么是会员模式，要么是普通的计划表，要么是一些我不那么喜欢的APP</p><p>于是我思索一番，要不然自己写一个得了，虽然自己是学嵌入式的，但是AI当下，大不了我指挥AI开始操作，在我挑选代码平台的时候，想起来QT作为一个跨平台语言，还是我学过的C++，这样AI写完还能自己缝缝补补，妙哉</p><p>于是，这个历经五天完成第一代版本的破烂小APP出现了</p><hr><p>下面还是主要看看经过的历程，以及简单介绍并<del>学习一下AI写的框架</del></p><h2 id="B-环境搭建及AI使用过程"><a href="#B-环境搭建及AI使用过程" class="headerlink" title="B 环境搭建及AI使用过程"></a>B 环境搭建及AI使用过程</h2><p>这里的环境分为三部分，分别是QT环境，QT的Android环境，以及最终要的AI环境</p><hr><h3 id="B-1-QT环境"><a href="#B-1-QT环境" class="headerlink" title="B.1 QT环境"></a>B.1 QT环境</h3><p>QT的环境比较简单，只要去官网下载然后安装，只用注意三点:</p><blockquote><ol><li>QT对于 QT5.15/QT6版本不再支持离线安装包安装，我这里使用的是 <strong>QT5.12.8</strong>,下载方式网上有很多，这里就不多说了； 注意,如果QT的版本为5.9+,那么自带Android构建工具链，安卓环境配置会非常简单</li><li>不要下载 <strong>QT 5.12.9</strong>，问就是我在这个版本安装Android环境卡了很久还是失败了</li><li>下载完QT之后，在安装组件的界面需要勾选 <strong>qt for android</strong>套件，如果已经安装过QT了，可以去安装目录下的<code>MaintenanceTool.exe</code>，运行这个程序重新添加套件</li></ol></blockquote><hr><h3 id="B-2-QT-Android环境"><a href="#B-2-QT-Android环境" class="headerlink" title="B.2 QT-Android环境"></a>B.2 QT-Android环境</h3><p>对于低版本的QT，在构建Android环境的时候 需要准备一些东西，比如Java，android SDK、NDK,如果手边没有设备还要下个安卓的虚拟机方便调试代码</p><p>但是这些环境不是什么版本都可以，需要根据你QT的具体版本来选择，我最开始的环境配置参考的别人的blog <a href="https://blog.csdn.net/wangyachao0803/article/details/139959614"><strong>CSDN Qt 5.14.2+Android环境搭建</strong></a></p><p>这里的具体配置大家可以安装QT之后参考如下路径查找一个配置文件:<br><code>C:\Users\Mouse\AppData\Roaming\QtProject\qtcreator\android\sdk_definitions.json</code></p><p>给出我的文件具体内容:</p><div class="code-container" data-rel="Json"><figure class="iseeu highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"common"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">        <span class="attr">"sdk_tools_url"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">            <span class="attr">"linux"</span><span class="punctuation">:</span> <span class="string">"https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"linux_sha256"</span><span class="punctuation">:</span> <span class="string">"92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"windows"</span><span class="punctuation">:</span> <span class="string">"https://dl.google.com/android/repository/sdk-tools-windows-4333796.zip"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"windows_sha256"</span><span class="punctuation">:</span> <span class="string">"7e81d69c303e47a4f0e748a6352d85cd0c8fd90a5a95ae4e076b5e5f960d3c7a"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"mac"</span><span class="punctuation">:</span> <span class="string">"https://dl.google.com/android/repository/sdk-tools-darwin-4333796.zip"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"mac_sha256"</span><span class="punctuation">:</span> <span class="string">"ecb29358bc0f13d7c2fa0f9290135a5b608e38434aad9bf7067d0252c160853e"</span></span><br><span class="line">        <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">"sdk_essential_packages"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">            <span class="attr">"default"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">"platform-tools"</span><span class="punctuation">,</span> <span class="string">"platforms;android-29"</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"linux"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"mac"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"windows"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">"extras;google;usb_driver"</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">}</span></span><br><span class="line">    <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"specific_qt_versions"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">{</span></span><br><span class="line">            <span class="attr">"versions"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">"default"</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"sdk_essential_packages"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">"build-tools;29.0.2"</span><span class="punctuation">,</span> <span class="string">"ndk;21.1.6352462"</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"ndk_path"</span><span class="punctuation">:</span> <span class="string">"ndk/21.1.6352462"</span></span><br><span class="line">        <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">        <span class="punctuation">{</span></span><br><span class="line">            <span class="attr">"versions"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">"5.12.[0-5]"</span><span class="punctuation">,</span> <span class="string">"5.13.[0-1]"</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"sdk_essential_packages"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">"build-tools;28.0.2"</span><span class="punctuation">,</span> <span class="string">"ndk;19.2.5345600"</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"ndk_path"</span><span class="punctuation">:</span> <span class="string">"ndk/19.2.5345600"</span></span><br><span class="line">        <span class="punctuation">}</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure></div><p>这里面会给出需要的SDK和NDK的参考版本信息，然后参考上面给的教程下载并配置到QT就行了</p><h3 id="B-3-AI环境"><a href="#B-3-AI环境" class="headerlink" title="B.3 AI环境"></a>B.3 AI环境</h3><p>因为申请了github的学生会员，所以可以免费使用 <strong>GitHub Copilot Pro</strong> 的权限，这也是我肆无忌惮使用AI写一个简单的软件的原因</p><p>当然，我只要成功写一个在手机上显示”Hello World”的程序，那么<strong>Agent</strong>就可以帮我写任何东西，<del>只要我给它一份初始3000字的提示词，以及高达几万的补充提示词，还要负责bug测试即可</del>，这样就可以简单的得到一份APP啦，完结撒花！</p><hr><h3 id="B-4-使用AI"><a href="#B-4-使用AI" class="headerlink" title="B.4 使用AI"></a>B.4 使用AI</h3><p>一开始，我是直接详细描述我需要一个怎么样的APP，需要哪些界面，每个界面是干什么的，以及我自定义的一个结构体，用来存储相关数据;</p><p>但是我发现，如果是一个泛化的目标，AI执行下来不论是逻辑还是界面都差强人意，没有达到我的预期</p><p>于是我在这个的基础上:</p><p><strong>单独描述每个界面</strong>: 登录界面，预加载界面，进入之后的主界面，导航栏界面，个人中心界面…… </p><p><strong>单独描述每个逻辑内容</strong>：登录的校验逻辑，个人中心的信息保存逻辑，总计划(自定义的结构体数据)的保存和修改逻辑，对于预览界面的滑动逻辑….. </p><p><strong>单独描述参数</strong>：界面各个的高度为整个屏幕的几分之几，滑动的灵敏度参数，保存的时机…..</p><p><strong>单独描述某些功能的实现方式</strong>：保存文件，从相册选取照片，下载东西 应该调用Android哪些接口 </p><p><strong>单独描述网络相关的内容</strong>: 生成应用端和服务端的相关文件，配置文件，执行文件(然后自行部署到云服务器上 ps：这个网络部分的协议我觉得AI生成的还是非常完善的，基本问题不大)</p><p>再一步步的纠正下，勉强达到了预期，但是又有一个大问题，就是最开始我并没有要求AI并没为我创建一个合适的架构来分隔不同功能的代码，<strong>它将所有界面功能写在了两三个文件里面</strong>，比如”mainwindow.cpp”，同时除了函数命名外，基本没有适合阅读的注释，使得代码臃肿难以阅读</p><p>于是我大手一挥，<del>给AI发去指令 “在保持当前逻辑功能和界面显示不变的情况下，重构代码，让其分层更有逻辑，并且补充每个函数的注释，注释不能使用泛化语句，必须具体”</del>  </p><p>不愧是我，轻轻松松解决了上面的问题</p><p>吸取之前的教训，我认为直接让AI写一个功能完善的APP还是有点困难 ps：当然，未来的AI大人一定可以的</p><p>所以后面的非健身主题的软件我打算自己动点手，防止AI全盘介入，可能效率和界面美观上可以更优一点</p><hr><h2 id="C-APP架构介绍"><a href="#C-APP架构介绍" class="headerlink" title="C APP架构介绍"></a>C APP架构介绍</h2><p>因为健身模块部分基本为AI完成，我主要阅读了一下整体的布局和功能，然后修改了一下架构，下面通过软件整体、主题功能、网络功能三个部分介绍一下</p><hr><h3 id="C-1-软件介绍"><a href="#C-1-软件介绍" class="headerlink" title="C.1 软件介绍"></a>C.1 软件介绍</h3><p>正如我开头所说，这是一个<strong>计划类的记录软件APP</strong>，我目前将它分成了三个主题模式，其一便是已经有了雏形的健身模式，剩余两个分别是 <strong>学习模式</strong> 和 <strong>普通模式</strong></p><p>下面主要给出 <strong>工程架构</strong> emm 看起来还是太臃肿了，但凑合看看吧</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line">Mouse_Plan/</span><br><span class="line">|-- main.cpp                         # Qt 应用入口</span><br><span class="line">|-- mainwindow.h                     # MainWindow 类声明</span><br><span class="line">|-- mainwindow.cpp                   # MainWindow 壳文件（实现已迁移到 modules）</span><br><span class="line">|-- mainwindow.ui                    # Qt Designer UI 文件</span><br><span class="line">|-- appdata.h                        # 数据模型与存储接口</span><br><span class="line">|-- appdata.cpp                      # 本地存储与 JSON 序列化</span><br><span class="line">|-- img.qrc                          # Qt 资源索引</span><br><span class="line">|-- Mouse_Plan_2.pro                 <span class="meta"># qmake 项目文件</span></span><br><span class="line">|-- Mouse_Plan_2.pro.user            # Qt Creator 本地用户配置</span><br><span class="line">|-- Mouse_Plan_2.code-workspace      # VS Code 工作区文件</span><br><span class="line">|-- README.md                        # 项目架构文档</span><br><span class="line">|-- .vscode/                         # 本地编辑器配置</span><br><span class="line">|-- config/                          # 全局相关的配置文件</span><br><span class="line">|-- img/                             # 图片资源</span><br><span class="line">|-- android/                         # Android 打包配置--略</span><br><span class="line"></span><br><span class="line">|</span><br><span class="line">|-- modules/                                            # 分层应用代码（按 common/themes/ui 拆分）</span><br><span class="line">|   |-- common/                                         # 跨主题通用能力层</span><br><span class="line">|   |   |-- agreement/                                  # 协议文本加载与模式切换（本地/在线）</span><br><span class="line">|   |   |   |-- agreement_text_loader.h                 # 协议加载器接口</span><br><span class="line">|   |   |   \-- agreement_text_loader.cpp               # 协议文件读取与回退逻辑实现</span><br><span class="line">|   |   |-- config/                                     # 公共配置常量</span><br><span class="line">|   |   |   \-- network_config.h                        # 网络地址与接口配置常量</span><br><span class="line">|   |   |-- theme/                                      # 主题策略与可用性控制</span><br><span class="line">|   |   |   |-- theme_feature_gate.h                    # 主题开关/可用性判断接口</span><br><span class="line">|   |   |   |-- theme_feature_gate.cpp                  # 主题可用性拦截实现</span><br><span class="line">|   |   |   |-- theme_strategy_factory.h                # 主题策略工厂接口</span><br><span class="line">|   |   |   \-- theme_strategy_factory.cpp              # 主题策略分发与构建实现</span><br><span class="line">|   |   |-- ui/                                         # 公共 UI 构建与弹窗工具</span><br><span class="line">|   |   |   |-- common_ui_pages.cpp                     # 通用页面片段创建</span><br><span class="line">|   |   |   |-- runtime_dialog_helpers.h                # 运行时弹窗工具接口</span><br><span class="line">|   |   |   \-- runtime_dialog_helpers.cpp              # 运行时弹窗工具实现</span><br><span class="line">|   |   \-- update/                                     # 应用更新辅助能力</span><br><span class="line">|   |       |-- update_client_helper.h                  # 更新客户端接口</span><br><span class="line">|   |       \-- update_client_helper.cpp                # 更新检查与下载辅助实现</span><br><span class="line">|   |           </span><br><span class="line">|   |-- themes/                                         # 主题业务层（按主题拆分）</span><br><span class="line">|   |   \-- fitness/                                    # 健身主题业务</span><br><span class="line">|   |       |-- calendar/                               # 日历标记与日期展示</span><br><span class="line">|   |       |   |-- fitness_calendar_mark_builder.h     # 日历标记构建接口</span><br><span class="line">|   |       |   \-- fitness_calendar_mark_builder.cpp   # 日历训练/休息标记生成</span><br><span class="line">|   |       |-- data/                                   # 健身主题数据模型与仓储</span><br><span class="line">|   |       |   |-- fitness_data_models.h               # 健身业务数据结构</span><br><span class="line">|   |       |   |-- fitness_data_repository.h           # 数据仓储接口</span><br><span class="line">|   |       |   \-- fitness_data_repository.cpp         # 数据读取与聚合实现</span><br><span class="line">|   |       |-- network/                                # 健身主题网络接口定义</span><br><span class="line">|   |       |   |-- fitness_online_api.h                # 在线 API 声明</span><br><span class="line">|   |       |   \-- fitness_online_api.cpp              # 在线 API 常量实现</span><br><span class="line">|   |       \-- plan/                                   # 计划与记录核心流程</span><br><span class="line">|   |           |-- fitness_plan_flow_helper.h          # 计划流程辅助接口</span><br><span class="line">|   |           |-- fitness_plan_flow_helper.cpp        # 计划流程计算实现</span><br><span class="line">|   |           |-- fitness_plan_runtime.cpp            # 计划运行时公共逻辑</span><br><span class="line">|   |           |-- fitness_plan_item_actions.cpp       # 计划项操作（完成/忽略/编辑）</span><br><span class="line">|   |           |-- fitness_training_record_actions.cpp # 训练记录提交与补录</span><br><span class="line">|   |           \-- mainwindow_fitness_plan_manager.cpp # 计划管理页与主窗口联动</span><br><span class="line">|   |</span><br><span class="line">|   \-- ui/                                             # 主窗口交互层（按页面功能拆分）</span><br><span class="line">|       |-- common/                                     # 启动、登录后流程等主线逻辑</span><br><span class="line">|       |   |-- mainwindow_bootstrap.cpp                # MainWindow 初始化与启动流程</span><br><span class="line">|       |   \-- mainwindow_session_flow.cpp             # 会话流程、页面切换与状态同步</span><br><span class="line">|       |-- home/                                       # 首页相关实现预留目录</span><br><span class="line">|       |-- login/                                      # 登录与注册流程</span><br><span class="line">|       |   |-- login_register_flow.h                   # 登录/注册流程接口</span><br><span class="line">|       |   \-- login_register_flow.cpp                 # 登录/注册流程实现</span><br><span class="line">|       |-- profile/                                    # 个人中心相关交互</span><br><span class="line">|       |   |-- profile_interaction_helper.h            # 个人中心交互辅助接口</span><br><span class="line">|       |   |-- profile_interaction_helper.cpp          # 个人中心交互辅助实现</span><br><span class="line">|       |   \-- mainwindow_profile_actions.cpp          # 主窗口个人页动作实现</span><br><span class="line">|       \-- theme/                                      # 主题切换与账号主题行为</span><br><span class="line">|           |-- mainwindow_theme_actions.cpp            # 主窗口主题相关动作实现</span><br><span class="line">|           \-- theme_account_actions.cpp               # 主题与账号联动动作实现</span><br><span class="line">|</span><br><span class="line">|-- MousePlan_Server/                # Node.js 后端服务</span><br><span class="line">|   |-- .env</span><br><span class="line">|   |-- .gitignore</span><br><span class="line">|   |-- .htaccess</span><br><span class="line">|   |-- Mouse_Add_code              #添加注册码的服务器接口</span><br><span class="line">|   |-- package.json</span><br><span class="line">|   |-- package-lock.json</span><br><span class="line">|   |-- README.md                   #使用文档</span><br><span class="line">|   |-- src/</span><br><span class="line">|   |   |-- config.js</span><br><span class="line">|   |   |-- db.js                   </span><br><span class="line">|   |   \-- index.js</span><br><span class="line">|   |-- data/</span><br><span class="line">|   |   |-- .gitkeep</span><br><span class="line">|   |   |-- db.json                 # 服务器数据的json串</span><br><span class="line">|   |   \-- updates/                # APK/更新包投放目录</span><br><span class="line">|   \-- node_modules/               # 已安装 npm 依赖相关</span><br></pre></td></tr></table></figure></div><hr><p>软件的整体入口在<code>mainwindow_bootstrap.cpp</code> 文件里面,预加载的模块在<code>main.cpp</code>,整个软件目前的数据结构为都定义在<code>appdata</code>文件中  </p><p>程序通过一个预加载的界面，直接进入登录界面，登录界面判定成功后进入软件的主界面(新用户会先进入主题选择的界面，根据不同的选择，进入不同的Home界面)，不同主题拥有不同的功能</p><hr><h3 id="C-2-主题介绍"><a href="#C-2-主题介绍" class="headerlink" title="C.2 主题介绍"></a>C.2 主题介绍</h3><p>就是每个主题的具体功能和流程</p><hr><h4 id="C-2-1-主题一-健身主题"><a href="#C-2-1-主题一-健身主题" class="headerlink" title="C.2.1 主题一 健身主题"></a>C.2.1 主题一 健身主题</h4><p>默认主题就是健身主题，也是用AI写这个APP的最初目的，下面给出该主题的功能</p><p>对于本地模式：</p><ol><li><strong>总计划功能</strong>：设置单/多个健身总计划，分别包括练习的周期以及每天的具体训练内容，比如动作名称，热身和正式组的具体内容等，当然还有开始的时间和休息日的时间</li><li><strong>日历映射功能</strong>：可以选择设置的总计划为当前计划，然后该计划的内容会按照总计划预设映射到日历组件上，这样可以通过点击日历查看以后的计划，对于当天的内容会直接预览出来</li><li><strong>打卡功能</strong>：对于当天的每个项目，可以主动勾选打钩表示完成，当所有项目完成后，可以进行打卡提交记录，会在对应日期保存当天的训练记录和时间</li><li><strong>总计划导出/导入</strong>:总计划数据包可以导出，方便因特殊原因的总计划丢失</li></ol><p>在这三个的基础上，可以添加<strong>补录</strong>、<strong>间歇时间提醒</strong>、<strong>食物热量/营养计算</strong>、<strong>体重记录</strong>、<strong>总数据备份</strong>等功能</p><p>对于在线模式，在本地模式的基础上：</p><ol><li><strong>可以注册登录</strong>: 保留专门的ID，可以在不同设备上登录，同步数据 </li><li><strong>数据保存</strong>: 当打卡之后，相关数据会更新至服务器，这样即便卸载软件，更换手机数据也不会丢失</li><li><strong>在线计划</strong>: 用户可以分享或者上传自己的总计划至服务器，方便好友或者他人直接下载借鉴 (还没实现)</li></ol><p>注: 仅在打卡的时候会尝试保存一次数据，所以只要登录过一次(有登录记忆)，除了联网功能，可以当做本地模式使用</p><hr><h4 id="C-3-2-主题三-学习主题"><a href="#C-3-2-主题三-学习主题" class="headerlink" title="C.3.2 主题三 学习主题"></a>C.3.2 主题三 学习主题</h4><p><del>还没有实现，有想法和建议可以告诉我</del></p><hr><h4 id="C-3-3-主题三-日常主题"><a href="#C-3-3-主题三-日常主题" class="headerlink" title="C.3.3 主题三 日常主题"></a>C.3.3 主题三 日常主题</h4><p><del>还没有实现，有想法和建议可以告诉我</del></p><hr><h3 id="C-3-云服务器网络服务相关介绍"><a href="#C-3-云服务器网络服务相关介绍" class="headerlink" title="C.3 云服务器网络服务相关介绍"></a>C.3 云服务器网络服务相关介绍</h3><p>我的小破服务器肯定没办法支持很多数据的并发和存储，所有我预留了服务器设置的接口，大家可以直接将服务器端的文件一键部署到自己的云服务器，然后在软件的”服务器设置”界面填入自己的服务器地址,具体的使用方法可以直接查看服务端文件的<code>README.md</code>文件或者简单参考下面的内容:</p><blockquote><p>Mouse Plan 提供了简单的服务器端接口，用户可将服务端文件部署到自己的云服务器，并在软件的“服务器设置”界面填入服务器地址</p></blockquote><h4 id="服务端功能"><a href="#服务端功能" class="headerlink" title="服务端功能"></a>服务端功能</h4><ol><li><p><strong>注册码管理</strong>：</p><ul><li>支持注册码的生成与验证。</li></ul></li><li><p><strong>数据同步</strong>：</p><ul><li>支持用户数据的上传与下载。</li></ul></li><li><p><strong>更新检查</strong>：</p><ul><li>检查是否有新版本可用。</li></ul></li></ol><h4 id="服务端部署方法"><a href="#服务端部署方法" class="headerlink" title="服务端部署方法"></a>服务端部署方法</h4><ol><li><p><strong>环境准备</strong>：</p><ul><li>安装 Node.js 和 npm。</li><li>确保服务器支持 HTTP 请求。</li></ul></li><li><p><strong>克隆代码仓库</strong>：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/Hunhaozi/MousePlan.git</span><br><span class="line"><span class="built_in">cd</span> MousePlan_Server</span><br></pre></td></tr></table></figure></div></li><li><p><strong>安装依赖</strong>：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install</span><br></pre></td></tr></table></figure></div></li><li><p><strong>配置环境变量</strong>：</p><ul><li><p>创建 <code>.env</code> 文件，设置以下内容：分别是服务器的端口号与数据存储位置</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">PORT=3000     </span><br><span class="line">DB_PATH=./data/db.json</span><br></pre></td></tr></table></figure></div></li></ul></li><li><p><strong>启动服务</strong>：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm start</span><br></pre></td></tr></table></figure></div><p>服务启动后，访问 <code>http://localhost:3000</code>，如果配置为自己服务器，那么将该地址直接填入APP的服务器设置选项中则可以直接使用</p></li><li><p><strong>部署到生产环境</strong>：</p><ul><li><p>使用 PM2 或其他进程管理工具保持服务运行：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install -g pm2</span><br><span class="line">pm2 start src/index.js --name MousePlan_Server</span><br></pre></td></tr></table></figure></div></li><li><p>配置 Nginx 或 Apache 反向代理以支持 HTTPS。</p></li></ul></li></ol><h2 id="F-APP使用方法相关"><a href="#F-APP使用方法相关" class="headerlink" title="F APP使用方法相关"></a>F APP使用方法相关</h2><p>当前的APP内容极度不完善，因为AI写的还是有点小乱，可能还会有很多bug，下面我会慢慢添加修改功能和修复bug，<del>一定不是因为我的Copilot Pro额度用完了</del></p><p>对于软件的登录，本地模式可以直接登录使用，对于在线模式账号的发放并不是使用短信验证码或者其它第三方登录，而是使用简单的<strong>注册码模式</strong>，如果感兴趣的可以直接下方留言或者发邮箱找我要注册码，然后体验当前的测试版本APP</p><h2 id="G-文件地址"><a href="#G-文件地址" class="headerlink" title="G 文件地址"></a>G 文件地址</h2><blockquote><p><a class="link" href="https://github.com/Hunhaozi/MousePlan">Github 项目地址<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a><br><a class="link" href="https://github.com/Hunhaozi/MousePlan">MousePlan APP下载地址 – 也是Github<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p></blockquote><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><strong>AI 大人天下无敌</strong></li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#52;&#54;&#55;&#48;&#x32;&#x33;&#54;&#x32;&#x40;&#113;&#113;&#x2e;&#x63;&#x6f;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">突然想看看安卓应用用QT写是什么感觉，同时也是看看AI智能体对于应用的冲击力......</summary>
    
    
    
    <category term="QT" scheme="https://blog.haozi-haozi.cn/categories/QT/"/>
    
    
    <category term="Android" scheme="https://blog.haozi-haozi.cn/tags/Android/"/>
    
    <category term="MousePlan" scheme="https://blog.haozi-haozi.cn/tags/MousePlan/"/>
    
    <category term="AI" scheme="https://blog.haozi-haozi.cn/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>windows下推送blog至服务器的方法</title>
    <link href="https://blog.haozi-haozi.cn/2026/03/15/blog_daily_pushblog/"/>
    <id>https://blog.haozi-haozi.cn/2026/03/15/blog_daily_pushblog/</id>
    <published>2026-03-15T13:22:00.000Z</published>
    <updated>2026-03-24T08:57:28.204Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-直接给出结论"><a href="#A-直接给出结论" class="headerlink" title="A 直接给出结论"></a>A 直接给出结论</h2><p>ps：如果能看到这篇文章，就是推送成功了</p><p>当然,这个适合和我一样已经准备好私钥的情况下直接推送，如果是账号登录可以参考下面的链接 <a href="https://bbbvin.blog.haozi-haozi.cn/2026/03/13/%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%8E%A8%E9%80%811/"><strong>scp指令推送内容到服务器</strong></a></p><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ===== Hexo + SCP 自动部署（Windows）=====</span></span><br><span class="line"></span><br><span class="line">HEXO = npx hexo</span><br><span class="line"></span><br><span class="line"><span class="comment"># Windows OpenSSH</span></span><br><span class="line">SCP = C:/Windows/System32/OpenSSH/scp.exe</span><br><span class="line">SSH = C:/Windows/System32/OpenSSH/ssh.exe</span><br><span class="line"></span><br><span class="line"><span class="comment"># 服务器信息 用户名和服务器地址</span></span><br><span class="line">REMOTE_USER = root  </span><br><span class="line">REMOTE_HOST = xxx.xxx.xxx.xxx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 目录定义</span></span><br><span class="line">REMOTE_PARENT = /www/wwwroot/blog</span><br><span class="line">REMOTE_ACTIVE = <span class="variable">$(REMOTE_PARENT)</span>/pub        </span><br><span class="line">REMOTE_UPLOAD = <span class="variable">$(REMOTE_PARENT)</span>/public</span><br><span class="line"></span><br><span class="line"><span class="comment"># 本机私钥（纯路径）</span></span><br><span class="line">KEY_FILE = C:/password/yun.pem</span><br><span class="line"></span><br><span class="line"><span class="comment"># SSH 参数</span></span><br><span class="line">SSH_OPTS = -i <span class="string">"<span class="variable">$(KEY_FILE)</span>"</span> -o StrictHostKeyChecking=accept-new</span><br><span class="line"></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: help build test-ssh backup upload switch push deploy</span></span><br><span class="line"></span><br><span class="line"><span class="section">help:</span></span><br><span class="line"> @echo Available targets:</span><br><span class="line"> @echo   make test-ssh   - 测试免密连接</span><br><span class="line"> @echo   make build      - 仅本地构建</span><br><span class="line"> @echo   make push       - 构建并发布</span><br><span class="line"> @echo   make deploy     - 同 push</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1) 本地构建</span></span><br><span class="line"><span class="section">build:</span></span><br><span class="line"> <span class="variable">$(HEXO)</span> clean</span><br><span class="line"> <span class="variable">$(HEXO)</span> g</span><br><span class="line"> <span class="variable">$(HEXO)</span> d</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2) 测试 SSH</span></span><br><span class="line"><span class="section">test-ssh:</span></span><br><span class="line"> <span class="string">"<span class="variable">$(SSH)</span>"</span> <span class="variable">$(SSH_OPTS)</span> <span class="variable">$(REMOTE_USER)</span>@<span class="variable">$(REMOTE_HOST)</span> <span class="string">"echo SSH OK"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3) 备份远端 pub -&gt; pub_时间戳</span></span><br><span class="line"><span class="section">backup:</span></span><br><span class="line"> <span class="string">"<span class="variable">$(SSH)</span>"</span> <span class="variable">$(SSH_OPTS)</span> <span class="variable">$(REMOTE_USER)</span>@<span class="variable">$(REMOTE_HOST)</span> <span class="string">"mkdir -p '<span class="variable">$(REMOTE_PARENT)</span>' &amp;&amp; if [ -d '<span class="variable">$(REMOTE_ACTIVE)</span>' ]; then ts=$$(date +%Y%m%d_%H%M%S); mv '<span class="variable">$(REMOTE_ACTIVE)</span>' '<span class="variable">$(REMOTE_PARENT)</span>/pub_'\"$$ts\"; fi"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4) 上传本地 public 到远端父目录</span></span><br><span class="line"><span class="section">upload:</span></span><br><span class="line"> <span class="string">"<span class="variable">$(SCP)</span>"</span> <span class="variable">$(SSH_OPTS)</span> -r public <span class="variable">$(REMOTE_USER)</span>@<span class="variable">$(REMOTE_HOST)</span>:<span class="variable">$(REMOTE_PARENT)</span>/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5) 远端 public 改名为 pub</span></span><br><span class="line"><span class="section">switch:</span></span><br><span class="line"> <span class="string">"<span class="variable">$(SSH)</span>"</span> <span class="variable">$(SSH_OPTS)</span> <span class="variable">$(REMOTE_USER)</span>@<span class="variable">$(REMOTE_HOST)</span> <span class="string">"if [ -d '<span class="variable">$(REMOTE_UPLOAD)</span>' ]; then mv '<span class="variable">$(REMOTE_UPLOAD)</span>' '<span class="variable">$(REMOTE_ACTIVE)</span>'; else echo 'upload failed: <span class="variable">$(REMOTE_UPLOAD)</span> not found'; exit 1; fi"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 一键发布</span></span><br><span class="line"><span class="section">push: build backup upload switch</span></span><br><span class="line"> @echo Deploy done.</span><br><span class="line"></span><br><span class="line"><span class="section">deploy: push</span></span><br></pre></td></tr></table></figure></div><p>然后再本地更新完博客就能更方便的推送啦，直接<code>make push</code></p><p>ps:如果make指令无法使用，检查是否安装了<code>MinGW</code>或<code>msys64</code>,如果安装了，添加其的bin文件至系统环境变量，然后修改这两个工具bin文件下的<code>mingw32-make.exe</code>文件，直接修改成<code>make.exe</code>即可</p><p><del>也可以直接用mingw32-make push 指令</del></p><p>完结撒花~</p><blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#51;&#49;&#x34;&#54;&#x37;&#x30;&#x32;&#x33;&#x36;&#x32;&#64;&#113;&#x71;&#x2e;&#99;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">补充一下windows推送博客的Makefile方法</summary>
    
    
    
    <category term="daily" scheme="https://blog.haozi-haozi.cn/categories/daily/"/>
    
    
    <category term="daily" scheme="https://blog.haozi-haozi.cn/tags/daily/"/>
    
    <category term="blog" scheme="https://blog.haozi-haozi.cn/tags/blog/"/>
    
  </entry>
  
  <entry>
    <title>ZYNQ -- 裸机移植 micropython</title>
    <link href="https://blog.haozi-haozi.cn/2026/01/09/embedded_ZYNQ_micropython/"/>
    <id>https://blog.haozi-haozi.cn/2026/01/09/embedded_ZYNQ_micropython/</id>
    <published>2026-01-09T11:28:00.000Z</published>
    <updated>2026-03-24T08:58:44.585Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前言"><a href="#A-前言" class="headerlink" title="A 前言"></a>A 前言</h2><p>这阵子做一个项目,需要用到ZYNQ,使用的环境是Xilinx + Vivado 2018.3 SDK开发，开发板是ZYNQ的7010/7020系列，这里主要记录移植micropython过程，移植过程还使用了 xftp(虚拟机传输文件) vscode(ssh插件远程连接) 任意串口调试助手 VMware(虚拟机 使用的是ubuntu16.04)<br>(ps:<strong>不是FPGA方向</strong>)</p><hr><h2 id="B-工程模版相关"><a href="#B-工程模版相关" class="headerlink" title="B 工程模版相关"></a>B 工程模版相关</h2><p>这里的工程模版使用的是串口+GPIO输出即可(我这里选择的是一个串口，和三位的LED输出，大家可以参考之间的ZYNQ文章配置(emm也有可能这个文章还没写)，注意要测试好串口能成功输出):  （ps:<strong>建立在能够配置好引脚并导出到SDK开发的前提下）</strong></p><p>奥对，这里简单介绍一下micropython：</p><blockquote><p>MicroPython​ 是 Python 3 语言的精简高效实现，专为微控制器和资源受限的嵌入式设备设计。它由 Damien P. George 于 2013 年发起，并在 Kickstarter 上成功众筹，如今已成为物联网、教育、快速原型开发等领域的热门工具。其核心特点包括：</p></blockquote><ul><li><p>语法兼容 Python 3：支持 Python 3.4+ 的主要语法特性（如异常处理、with/yield from/async/await等），内置常用数据类型（str、bytes、list、dict、set等）和标准库模块（sys、time、struct等）。</p></li><li><p>交互式解释器（REPL）：通过串口连接可直接进入交互环境，实时执行代码、调试外设，大幅降低开发门槛。</p></li><li><p>硬件抽象层：对 GPIO、UART、I2C、SPI、PWM、ADC 等常见外设提供统一 API（主要通过 machine模块），兼顾易用性与底层控制能力。</p></li><li><p>跨平台支持：已适配 STM32、ESP32/8266、Raspberry Pi Pico（RP2040）、K210/K230 等主流 MCU，官方提供多版本预编译固件</p></li></ul><p>这里因为是ZYNQ的裸机移植(双核ARM Cortex-A9)，所以我们选择最简单的ARM系列的模版即可</p><hr><h2 id="C-移植过程"><a href="#C-移植过程" class="headerlink" title="C 移植过程"></a>C 移植过程</h2><h3 id="C-1-获取micropython的源码"><a href="#C-1-获取micropython的源码" class="headerlink" title="C.1 获取micropython的源码"></a>C.1 获取micropython的源码</h3><p>这里要先获取源码，必须要安装git(大家可以自行查找教程)，当然也可以去github在浏览器下载压缩包再传给虚拟机解压</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clonehttps://github.com/micropython/micropython </span><br></pre></td></tr></table></figure></div><p>然后就可以得到一个文件夹，主要介绍我们待会要使用的几个文件:</p><ul><li><p><strong>ports</strong> 针对不同微控制器和系统的移植代码，是开发和应用的主要入口</p></li><li><p><strong>lib</strong> 存放芯片原厂SDK、底层驱动库等第三方库代码，为各端口提供底层支持</p></li><li><p><strong>py</strong> MicroPython的核心实现，包括编译器、运行时和核心库</p></li></ul><p>下面我们选择ports文件夹里面的<strong>bare-arm</strong> 这是一个极其精简的版本，我们在这基础上慢慢添加代码，也可以更好的理解移植过程</p><p>下面来介绍这里我们需要修改的文件:</p><blockquote><p><strong>配置文件</strong>: mpconfigport.h, mphalport.h 第一个文件是配置宏定义，第二个文件是我移植GPIO需要使用的接口函数定义<br><strong>核心代码</strong> main.c, lib.c, system.c 分别是入口，依赖和配置文件，后面我们移植的端口都写在后面创建的mphalport.c文件里面，main.c我们使用创建ZYNQ工程时的即可，后期修改main.c为pymain.c，里面修改成交互的python代码入口<br><strong>构建文件</strong>  Makefile,编译生成qstr文件需要使用  注意这里的ld链接文件是给stm32使用的，我们移植到ZYNQ的SDK编译器上，是不需用这个文件的，不用管它就行</p></blockquote><hr><h3 id="C-2-根据模版修改配置文件"><a href="#C-2-根据模版修改配置文件" class="headerlink" title="C.2 根据模版修改配置文件"></a>C.2 根据模版修改配置文件</h3><p>现在开始修改配置文件，这里我们只保留最简的功能即可，也就是简单的python的代码运行,通过串口交互，GPIO控制。不包含操作系统，文件系统等</p><p>我们先修改 mpconfigport.h  修改成下面的文件，具体内容都有注释</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This file is part of the MicroPython project, http://micropython.org/</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The MIT License (MIT)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Copyright (c) 2014-2021 Damien P. George</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Permission is hereby granted, free of charge, to any person obtaining a copy</span></span><br><span class="line"><span class="comment"> * of this software and associated documentation files (the "Software"), to deal</span></span><br><span class="line"><span class="comment"> * in the Software without restriction, including without limitation the rights</span></span><br><span class="line"><span class="comment"> * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span></span><br><span class="line"><span class="comment"> * copies of the Software, and to permit persons to whom the Software is</span></span><br><span class="line"><span class="comment"> * furnished to do so, subject to the following conditions:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The above copyright notice and this permission notice shall be included in</span></span><br><span class="line"><span class="comment"> * all copies or substantial portions of the Software.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span></span><br><span class="line"><span class="comment"> * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span></span><br><span class="line"><span class="comment"> * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</span></span><br><span class="line"><span class="comment"> * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</span></span><br><span class="line"><span class="comment"> * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</span></span><br><span class="line"><span class="comment"> * OUT OF OR IN CONNECTION WITH THE USE OR OTHER DEALINGS IN</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭 micropython 内置模块（避免 extmod 依赖）</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MICROPYTHON (0) <span class="comment">// 禁用 micropython 模块</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_BUILTINS    (1) <span class="comment">// 启用内置函数和异常</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_GC          (1) <span class="comment">// 启用垃圾回收模块</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_ENABLE_GC (1)      <span class="comment">// 启用垃圾回收功能，否则变量功能无法使用</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制 MicroPython 构建方式的选项</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用最小化的起始配置（禁用所有可选功能）</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_CONFIG_ROM_LEVEL                (MICROPY_CONFIG_ROM_LEVEL_MINIMUM)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 编译器配置</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_ENABLE_COMPILER                 (1) <span class="comment">// 启用编译器</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Python 内部特性</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) <span class="comment">// 详细错误报告</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_CPYTHON_COMPAT (0) <span class="comment">// 不与 CPython 严格兼容以节省空间</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_DEBUG_PRINTERS (1) <span class="comment">// 启用调试打印机</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 精细控制 Python 内置功能、类、模块等</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_SYS                          (0) <span class="comment">// 禁用 sys 模块</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 为 GPIO 控制添加的配置</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE                      (1) <span class="comment">// 启用 machine 模块（包含GPIO控制）</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE_PIN_MAKE_NEW         (1) <span class="comment">// 启用创建新 Pin 对象的功能</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE_PIN_READ_METHOD      (1) <span class="comment">// 启用引脚读取方法</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE_PIN_WRITE_METHOD     (1) <span class="comment">// 启用引脚写入方法</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 特定机器的类型定义</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">long</span> <span class="type">mp_off_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 需要提供 alloca() 函数的声明/定义</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;alloca.h&gt;</span></span></span><br></pre></td></tr></table></figure></div><hr><h3 id="C-3-编译源码，得到QSTR输出文件"><a href="#C-3-编译源码，得到QSTR输出文件" class="headerlink" title="C.3 编译源码，得到QSTR输出文件"></a>C.3 编译源码，得到QSTR输出文件</h3><p>如果直接移植，因为我们是移植到SDK开发，所以会缺少一点输出文件，所以这里我们在Linux环境下(win也行)，首先在主目录下执行下面代码</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在 micropython/ 根目录下执行，编译 mpy-cross交叉编译器，这是后续编译端口固件的前提</span></span><br><span class="line">make -C mpy-cross</span><br><span class="line"><span class="built_in">cd</span> ports/bare-arm/  </span><br><span class="line">make</span><br><span class="line"></span><br><span class="line"><span class="comment"># make clean -C mpy-cross 这个是清除编译的指令</span></span><br></pre></td></tr></table></figure></div><p>然后就会在ports/bare-arm/ 目前下生成一个build文件，其中build/genhdr/qstrdefs.generated.h 文件是我需要的输出文件，否则后面编译会报错，注意后面如果修改了配置文件，也需要重新编译生成这个输出文件</p><p>这里在源码方面的准备就结束了，我们下面将源码导入SDK中</p><hr><h3 id="C-4-在SDK中导入源码并修改"><a href="#C-4-在SDK中导入源码并修改" class="headerlink" title="C.4 在SDK中导入源码并修改"></a>C.4 在SDK中导入源码并修改</h3><p>将工程创建完成之后，先测试简单的串口输出和点灯是否正常(创建一个最简的工程)，如果正常再进行下面的步骤</p><p>首先右键工程，-New-Floder 添加一个文件夹:MicroPython</p><p>然后右键添加的文件夹,选择Import-General-File-System 然后店家上面的BroWse，选择分别添加下面列出的文件夹，最后点击Finish添加</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000002.webp" width="500"><p>这里需要添加的文件夹: 主文件夹下的: lib py 以及 ports/bare-arm</p><p>添加完毕之后目录结构如图</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000003.webp" width="500"><p>注意，这里添加的是编译之后的bare-arm文件夹，即里面的build文件也是新生成的，后期如果涉及重新编译，那么也要重新替换这个文件</p><p>下面会报错，显示找不到头文件等错误，所以这里我们添加一下环境路径（如图所示，右键工程，然后按照图中选择刚刚添加的路径以及根目录），这样路径报错就解决了</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000004.webp" width="500"><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000005.webp" width="500"><p>添加完毕之后如下图所示，如果还有路径有问题，比如说有的地方包含了头文件还是没有效果，可以尝试把左侧的Debug文件删除重新编译试试，或者修改头文件为完整路径名</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000007.webp" width="500"><p>然后编译会出现下面的报错，因为我们并没有包含extmod文件，这里我们通过SDK来自己实现有关GPIO的完整的HAL，也就是mp_hal_pin_write等函数，我们在创建一个文件bare-arm/mphalport.c中添加对GPIO的操作以及在mphalport中添加定义，实现简单的定时实现等，先给出空实现，让编译通过</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000006.webp" width="500"><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __MPHALPORT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __MPHALPORT_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/mpconfig.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 时间控制函数</span></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_ms</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_us</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_cpu</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_ms</span><span class="params">(<span class="type">mp_uint_t</span> ms)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_us</span><span class="params">(<span class="type">mp_uint_t</span> us)</span>;</span><br><span class="line"><span class="comment">// 标准输入输出函数</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_stdin_rx_chr</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_str</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_stdout_tx_strn</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_strn_cooked</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span>;</span><br><span class="line"><span class="type">uintptr_t</span> <span class="title function_">mp_hal_stdio_poll</span><span class="params">(<span class="type">uintptr_t</span> poll_flags)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// GPIO引脚操作函数（自定义GPIO API）</span></span><br><span class="line"><span class="comment">// 引脚对象类型定义</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">machine_pin_obj_t</span> <span class="title">mp_hal_pin_obj_t</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚读取</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_pin_read</span><span class="params">(<span class="type">mp_hal_pin_obj_t</span> *pin)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚写入</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_pin_write</span><span class="params">(<span class="type">mp_hal_pin_obj_t</span> *pin, <span class="type">int</span> value)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚模式设置</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_pin_open_set</span><span class="params">(<span class="type">void</span> *machine_pin, <span class="type">int</span> mode)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 系统控制函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_set_interrupt_char</span><span class="params">(<span class="type">char</span> c)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">uint64_t</span> <span class="title function_">mp_hal_time_ns</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在 mphalport.h 或相应移植文件中实现</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/stream.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/mphal.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xparameters.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xuartps.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 时间控制函数</span></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_ms</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回系统启动后的毫秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_us</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回系统启动后的微秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_cpu</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回CPU时钟计数（用于高精度计时）</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_ms</span><span class="params">(<span class="type">mp_uint_t</span> ms)</span> {</span><br><span class="line">    <span class="comment">// 阻塞延时毫秒</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_us</span><span class="params">(<span class="type">mp_uint_t</span> us)</span> {</span><br><span class="line">    <span class="comment">// 阻塞延时微秒</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标准输入输出函数</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_stdin_rx_chr</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// u8 ReceivedData;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// // 尝试接收一个字节</span></span><br><span class="line">    <span class="comment">// if (Uart1_ReceiveByte(&amp;ReceivedData)) {</span></span><br><span class="line">    <span class="comment">//     // 接收到数据，直接回显</span></span><br><span class="line">    <span class="comment">//     //XUartPs_SendByte(Uart1_Inst.Config.BaseAddress, ReceivedData);</span></span><br><span class="line">    <span class="comment">// return ReceivedData;</span></span><br><span class="line">    <span class="comment">// }</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span> <span class="comment">//空实现</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> mp_hal_stdout_tx_str(<span class="type">const</span> <span class="type">char</span> *str) {</span><br><span class="line">    <span class="comment">// 向标准输出发送字符串</span></span><br><span class="line"><span class="comment">//  printf("%.*s", strlen(str), str);</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> mp_hal_stdout_tx_strn(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len) {</span><br><span class="line">    <span class="comment">// 向标准输出发送指定长度字符串</span></span><br><span class="line"><span class="comment">//  printf("%.*s", len, str);</span></span><br><span class="line">    <span class="keyword">return</span> len;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_strn_cooked</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span> {</span><br><span class="line"><span class="comment">//  printf("%.*s", len, str);</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uintptr_t</span> <span class="title function_">mp_hal_stdio_poll</span><span class="params">(<span class="type">uintptr_t</span> poll_flags)</span> {</span><br><span class="line">    <span class="comment">// 检查标准输入/输出状态（用于非阻塞操作）</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚读取</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_pin_read</span><span class="params">(<span class="type">mp_hal_pin_obj_t</span> *pin)</span> {</span><br><span class="line">    <span class="comment">// 读取引脚电平</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚写入</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_pin_write</span><span class="params">(<span class="type">mp_hal_pin_obj_t</span> *pin, <span class="type">int</span> value)</span> {</span><br><span class="line">    <span class="comment">// 设置引脚电平</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚模式设置</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_pin_open_set</span><span class="params">(<span class="type">void</span> *machine_pin, <span class="type">int</span> mode)</span> {</span><br><span class="line">    <span class="comment">// 设置引脚模式（输入/输出等）</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 系统控制函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_set_interrupt_char</span><span class="params">(<span class="type">char</span> c)</span> {</span><br><span class="line">    <span class="comment">// 设置中断字符（如Ctrl+C）</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint64_t</span> <span class="title function_">mp_hal_time_ns</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回自纪元以来的纳秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后是要<strong>裁剪一些库</strong>，因为很多功能我们都用不着（还有一些数学库，SDK里面已经存在了），所以我们可以通过SDK直接选择让某些文件/文件夹不参加编译，如图，比如我们移除uzlib，因为这个和我们的功能无关，选择这个选项之后，全选，然后重新编译即可</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000008.webp" width="500"><p>这里我们选择将 <strong>uzlib re1.5 oofatfs mbedtls_errors crypto-algorithms libm libm_dbl</strong>都移除</p><p>然后找到<strong>py/gc.h</strong>在头文件里面添加: <code>#include "bare-arm/mpconfigport.h"</code></p><p>同时我们将将<strong>ststem.c</strong>文件清空，这里我们暂时不写代码，具体接口都先写在<strong>bare-arm/mphalport.c</strong>中,然后实现<strong>gc_collect()<strong>函数，这是垃圾回收的函数，如果不使用也可以空实现一下即可，或者和我一样用下面的代码替换</strong>system.c</strong>文件,后面再次make的时候，如果缺少gc_collect()也是空实现一下即可</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This file is part of the MicroPython project, http://micropython.org/</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The MIT License (MIT)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Copyright (c) 2021 Damien P. George</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Permission is hereby granted, free of charge, to any person obtaining a copy</span></span><br><span class="line"><span class="comment"> * of this software and associated documentation files (the "Software"), to deal</span></span><br><span class="line"><span class="comment"> * in the Software without restriction, including without limitation the rights</span></span><br><span class="line"><span class="comment"> * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span></span><br><span class="line"><span class="comment"> * copies of the Software, and to permit persons to whom the Software is</span></span><br><span class="line"><span class="comment"> * furnished to do so, subject to the following conditions:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The above copyright notice and this permission notice shall be included in</span></span><br><span class="line"><span class="comment"> * all copies or substantial portions of the Software.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span></span><br><span class="line"><span class="comment"> * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span></span><br><span class="line"><span class="comment"> * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</span></span><br><span class="line"><span class="comment"> * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</span></span><br><span class="line"><span class="comment"> * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</span></span><br><span class="line"><span class="comment"> * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//暂时为空</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/gc.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span>    <span class="comment">// 定义 uintptr_t 类型</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stddef.h&gt;</span>    <span class="comment">// 定义 size_t 类型</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 确保链接脚本中的符号正确定义</span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">int</span> _stack;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">int</span> _stack_end;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 辅助函数：获取当前所有寄存器的值并保存到数组中</span></span><br><span class="line"><span class="comment">// 这些寄存器可能包含当前函数调用上下文中的对象指针</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">gc_helper_get_regs</span><span class="params">(<span class="keyword">volatile</span> <span class="type">uintptr_t</span> arr[])</span> {</span><br><span class="line">    <span class="comment">// 使用扩展汇编明确告知编译器我们需要读取这些通用寄存器的值</span></span><br><span class="line">    __asm__ <span class="title function_">volatile</span> <span class="params">(</span></span><br><span class="line"><span class="params">        <span class="string">"mov %0, r0\n"</span>   <span class="comment">// 将寄存器 r0 的值存入数组第0个元素</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %1, r1\n"</span>   <span class="comment">// 将寄存器 r1 的值存入数组第1个元素</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %2, r2\n"</span>   <span class="comment">// ... 以此类推</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %3, r3\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %4, r4\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %5, r5\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %6, r6\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %7, r7\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %8, r8\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %9, r9\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %10, r10\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %11, r11\n"</span></span></span><br><span class="line"><span class="params">        <span class="string">"mov %12, r12\n"</span> <span class="comment">// 寄存器 r12</span></span></span><br><span class="line"><span class="params">        : <span class="string">"=r"</span> (arr[<span class="number">0</span>]), <span class="string">"=r"</span> (arr[<span class="number">1</span>]), <span class="string">"=r"</span> (arr[<span class="number">2</span>]), <span class="string">"=r"</span> (arr[<span class="number">3</span>]),</span></span><br><span class="line"><span class="params">          <span class="string">"=r"</span> (arr[<span class="number">4</span>]), <span class="string">"=r"</span> (arr[<span class="number">5</span>]), <span class="string">"=r"</span> (arr[<span class="number">6</span>]), <span class="string">"=r"</span> (arr[<span class="number">7</span>]),</span></span><br><span class="line"><span class="params">          <span class="string">"=r"</span> (arr[<span class="number">8</span>]), <span class="string">"=r"</span> (arr[<span class="number">9</span>]), <span class="string">"=r"</span> (arr[<span class="number">10</span>]), <span class="string">"=r"</span> (arr[<span class="number">11</span>]),</span></span><br><span class="line"><span class="params">          <span class="string">"=r"</span> (arr[<span class="number">12</span>])</span></span><br><span class="line"><span class="params">        : <span class="comment">// 没有输入操作数</span></span></span><br><span class="line"><span class="params">        : <span class="comment">// 没有显式声明的被破坏寄存器（编译器会自动处理）</span></span></span><br><span class="line"><span class="params">    )</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 实现 gc_collect 函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">gc_collect</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 启动垃圾回收过程</span></span><br><span class="line">    gc_collect_start();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 第二阶段：扫描根对象（寄存器和栈）</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 扫描CPU寄存器</span></span><br><span class="line">    <span class="comment">// 在当前栈帧上创建一个数组来保存寄存器值</span></span><br><span class="line">    <span class="keyword">volatile</span> <span class="type">uintptr_t</span> regs[<span class="number">13</span>]; <span class="comment">// 用于保存 R0-R12</span></span><br><span class="line">    gc_helper_get_regs(regs);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将保存寄存器值的数组本身也作为一个需要扫描的根区域</span></span><br><span class="line">    <span class="comment">// 因为数组里的每个元素都可能是某个Python对象的地址</span></span><br><span class="line">    gc_collect_root((<span class="type">void</span>**)regs, <span class="keyword">sizeof</span>(regs) / <span class="keyword">sizeof</span>(<span class="type">uintptr_t</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 扫描整个栈空间</span></span><br><span class="line">    <span class="comment">// 计算栈的大小（以 void* 为单位）。因为栈是向下生长的，所以：</span></span><br><span class="line">    <span class="comment">// 栈的起始地址（低地址）是 _stack_end</span></span><br><span class="line">    <span class="comment">// 栈的结束地址（高地址）是 _stack</span></span><br><span class="line">    <span class="type">uintptr_t</span> stack_top = (<span class="type">uintptr_t</span>)&amp;_stack_end;</span><br><span class="line">    <span class="type">uintptr_t</span> stack_bottom = (<span class="type">uintptr_t</span>)&amp;_stack;</span><br><span class="line">    <span class="comment">// 确保计算不会出现负数，进行安全转换</span></span><br><span class="line">    <span class="type">size_t</span> stack_size_in_words = (stack_bottom &gt; stack_top) ?</span><br><span class="line">                                (stack_bottom - stack_top) / <span class="keyword">sizeof</span>(<span class="type">void</span>*) : <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (stack_size_in_words &gt; <span class="number">0</span>) {</span><br><span class="line">        gc_collect_root((<span class="type">void</span>**)stack_top, stack_size_in_words);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 第三阶段：结束垃圾回收，进行实际的清扫工作</span></span><br><span class="line">    gc_collect_end();</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后我们将<strong>bar-arm/main.c</strong>修改成 <strong>bar-arm/pymain.c</strong>同时添加一个<strong>pymain.h</strong>文件，我们这里实现一些简单的初始化和接口</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * pymain.h</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *  Created on: 2026年1月11日</span></span><br><span class="line"><span class="comment"> *      Author: Mouse</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __PYMAIN_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __PYMAIN_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/compile.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">do_str</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *src, <span class="type">mp_parse_input_kind_t</span> input_kind)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">py_init</span><span class="params">()</span>;<span class="comment">//初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">py_uinit</span><span class="params">()</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">bare_main</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This file is part of the MicroPython project, http://micropython.org/</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The MIT License (MIT)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Copyright (c) 2014-2021 Damien P. George</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Permission is hereby granted, free of charge, to any person obtaining a copy</span></span><br><span class="line"><span class="comment"> * of this software and associated documentation files (the "Software"), to deal</span></span><br><span class="line"><span class="comment"> * in the Software without restriction, including without limitation the rights</span></span><br><span class="line"><span class="comment"> * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span></span><br><span class="line"><span class="comment"> * copies of the Software, and to permit persons to whom the Software is</span></span><br><span class="line"><span class="comment"> * furnished to do so, subject to the following conditions:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The above copyright notice and this permission notice shall be included in</span></span><br><span class="line"><span class="comment"> * all copies or substantial portions of the Software.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span></span><br><span class="line"><span class="comment"> * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span></span><br><span class="line"><span class="comment"> * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</span></span><br><span class="line"><span class="comment"> * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</span></span><br><span class="line"><span class="comment"> * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</span></span><br><span class="line"><span class="comment"> * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/compile.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/gc.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> HEAP_SIZE (32 * 1024) <span class="comment">// 定义内存大小</span></span></span><br><span class="line"><span class="type">static</span> <span class="type">char</span> heap[HEAP_SIZE];</span><br><span class="line"><span class="keyword">extern</span> <span class="type">int</span> <span class="title function_">Uart1_ReceiveByte</span><span class="params">(u8 *Data)</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">py_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    gc_init(heap, heap + <span class="keyword">sizeof</span>(heap)); <span class="comment">//内存初始化</span></span><br><span class="line">    mp_init(); <span class="comment">//python初始化</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">py_uinit</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    mp_deinit();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">char</span> *demo_single_input =</span><br><span class="line">    <span class="string">"print('hello world!', list(x + 1 for x in range(10)), end='eol\\n')"</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">char</span> *demo_file_input =</span><br><span class="line">    <span class="string">"import micropython\n"</span></span><br><span class="line">    <span class="string">"\n"</span></span><br><span class="line">    <span class="string">"print(dir(micropython))\n"</span></span><br><span class="line">    <span class="string">"\n"</span></span><br><span class="line">    <span class="string">"for i in range(10):\n"</span></span><br><span class="line">    <span class="string">"    print('iter {:08}'.format(i))"</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">do_str</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *src, <span class="type">mp_parse_input_kind_t</span> input_kind)</span> {</span><br><span class="line">    <span class="type">nlr_buf_t</span> nlr;</span><br><span class="line">    <span class="keyword">if</span> (nlr_push(&amp;nlr) == <span class="number">0</span>) {</span><br><span class="line">        <span class="comment">// Compile, parse and execute the given string.</span></span><br><span class="line">        <span class="type">mp_lexer_t</span> *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, <span class="built_in">strlen</span>(src), <span class="number">0</span>);</span><br><span class="line">        qstr source_name = lex-&gt;source_name;</span><br><span class="line">        <span class="type">mp_parse_tree_t</span> parse_tree = mp_parse(lex, input_kind);</span><br><span class="line">        <span class="type">mp_obj_t</span> module_fun = mp_compile(&amp;parse_tree, source_name, <span class="literal">true</span>);</span><br><span class="line">        mp_call_function_0(module_fun);</span><br><span class="line">        nlr_pop();</span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// Uncaught exception: print it out.</span></span><br><span class="line">        mp_obj_print_exception(&amp;mp_plat_print, (<span class="type">mp_obj_t</span>)nlr.ret_val);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Main entry point: initialise the runtime and execute demo strings.</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">bare_main</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    mp_init();</span><br><span class="line">    do_str(demo_single_input, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(demo_file_input, MP_PARSE_FILE_INPUT);</span><br><span class="line">    mp_deinit();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Called if an exception is raised outside all C exception-catching handlers.</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">nlr_jump_fail</span><span class="params">(<span class="type">void</span> *val)</span> {</span><br><span class="line">    <span class="keyword">for</span> (;;) {</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> NDEBUG</span></span><br><span class="line"><span class="comment">// Used when debugging is enabled.</span></span><br><span class="line"><span class="type">void</span> MP_WEAK __assert_func(<span class="type">const</span> <span class="type">char</span> *file, <span class="type">int</span> line, <span class="type">const</span> <span class="type">char</span> *func, <span class="type">const</span> <span class="type">char</span> *expr) {</span><br><span class="line">    <span class="keyword">for</span> (;;) {</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>最后我们还要补充前面空实现的端口，这里我们先补充串口输入输出即可，先来测试python的基础功能，注意这里使用的**Uart1_ReceiveByte()**函数是自己实现的（我写在主函数中了），大家还是参考之前的工程模版构建，下面给出我的主函数和修改后的mphalport.c文件</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在 mphalport.h 或相应移植文件中实现</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/stream.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/mphal.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xparameters.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xuartps.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 时间控制函数</span></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_ms</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回系统启动后的毫秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_us</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回系统启动后的微秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_cpu</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回CPU时钟计数（用于高精度计时）</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_ms</span><span class="params">(<span class="type">mp_uint_t</span> ms)</span> {</span><br><span class="line">    <span class="comment">// 阻塞延时毫秒</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_us</span><span class="params">(<span class="type">mp_uint_t</span> us)</span> {</span><br><span class="line">    <span class="comment">// 阻塞延时微秒</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标准输入输出函数</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_stdin_rx_chr</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    u8 ReceivedData;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 尝试接收一个字节</span></span><br><span class="line">    <span class="keyword">if</span> (Uart1_ReceiveByte(&amp;ReceivedData)) {</span><br><span class="line">        <span class="comment">// 接收到数据，直接回显</span></span><br><span class="line">        <span class="comment">//XUartPs_SendByte(Uart1_Inst.Config.BaseAddress, ReceivedData);</span></span><br><span class="line">    <span class="keyword">return</span> ReceivedData;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_str</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str)</span> {</span><br><span class="line">    <span class="comment">// 向标准输出发送字符串</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%.*s"</span>, <span class="built_in">strlen</span>(str), str);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_stdout_tx_strn</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span> {</span><br><span class="line">    <span class="comment">// 向标准输出发送指定长度字符串</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%.*s"</span>, len, str);</span><br><span class="line">    <span class="keyword">return</span> len;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_strn_cooked</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%.*s"</span>, len, str);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uintptr_t</span> <span class="title function_">mp_hal_stdio_poll</span><span class="params">(<span class="type">uintptr_t</span> poll_flags)</span> {</span><br><span class="line">    <span class="comment">// 检查标准输入/输出状态（用于非阻塞操作）</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚读取</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_pin_read</span><span class="params">(<span class="type">mp_hal_pin_obj_t</span> *pin)</span> {</span><br><span class="line">    <span class="comment">// 读取引脚电平</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚写入</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_pin_write</span><span class="params">(<span class="type">mp_hal_pin_obj_t</span> *pin, <span class="type">int</span> value)</span> {</span><br><span class="line">    <span class="comment">// 设置引脚电平</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引脚模式设置</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_pin_open_set</span><span class="params">(<span class="type">void</span> *machine_pin, <span class="type">int</span> mode)</span> {</span><br><span class="line">    <span class="comment">// 设置引脚模式（输入/输出等）</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 系统控制函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_set_interrupt_char</span><span class="params">(<span class="type">char</span> c)</span> {</span><br><span class="line">    <span class="comment">// 设置中断字符（如Ctrl+C）</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint64_t</span> <span class="title function_">mp_hal_time_ns</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回自纪元以来的纳秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/******************************************************************************</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* Copyright (C) 2009 - 2014 Xilinx, Inc.  All rights reserved.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* Permission is hereby granted, free of charge, to any person obtaining a copy</span></span><br><span class="line"><span class="comment">* of this software and associated documentation files (the "Software"), to deal</span></span><br><span class="line"><span class="comment">* in the Software without restriction, including without limitation the rights</span></span><br><span class="line"><span class="comment">* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span></span><br><span class="line"><span class="comment">* copies of the Software, and to permit persons to whom the Software is</span></span><br><span class="line"><span class="comment">* furnished to do so, subject to the following conditions:</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* The above copyright notice and this permission notice shall be included in</span></span><br><span class="line"><span class="comment">* all copies or substantial portions of the Software.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* Use of the Software is limited solely to applications:</span></span><br><span class="line"><span class="comment">* (a) running on a Xilinx device, or</span></span><br><span class="line"><span class="comment">* (b) that interact with a Xilinx device through a bus or interconnect.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span></span><br><span class="line"><span class="comment">* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span></span><br><span class="line"><span class="comment">* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL</span></span><br><span class="line"><span class="comment">* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,</span></span><br><span class="line"><span class="comment">* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF</span></span><br><span class="line"><span class="comment">* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE</span></span><br><span class="line"><span class="comment">* SOFTWARE.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* Except as contained in this notice, the name of the Xilinx shall not be used</span></span><br><span class="line"><span class="comment">* in advertising or otherwise to promote the sale, use or other dealings in</span></span><br><span class="line"><span class="comment">* this Software without prior written authorization from Xilinx.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">******************************************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * helloworld.c: simple test application</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This application configures UART 16550 to baud rate 9600.</span></span><br><span class="line"><span class="comment"> * PS7 UART (Zynq) is not initialized by this application, since</span></span><br><span class="line"><span class="comment"> * bootrom/bsp configures it to baud rate 115200</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * ------------------------------------------------</span></span><br><span class="line"><span class="comment"> * | UART TYPE   BAUD RATE                        |</span></span><br><span class="line"><span class="comment"> * ------------------------------------------------</span></span><br><span class="line"><span class="comment"> *   uartns550   9600</span></span><br><span class="line"><span class="comment"> *   uartlite    Configurable only in HW design</span></span><br><span class="line"><span class="comment"> *   ps7_uart    115200 (configured by bootrom/bsp)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xparameters.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xuartps.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xuartps_hw.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"platform.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xgpio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span>  <span class="comment">// for sleep()</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xil_printf.h"</span></span></span><br><span class="line"><span class="comment">// MicroPython 头文件</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bare-arm/pymain.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LED_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID</span></span><br><span class="line">XGpio LEDInst;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 串口1设备ID</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UART1_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 全局串口实例</span></span><br><span class="line">XUartPs Uart1_Inst;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输入缓冲区大小</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> INPUT_BUFFER_SIZE 256</span></span><br><span class="line"><span class="type">static</span> <span class="type">char</span> input_buffer[INPUT_BUFFER_SIZE];</span><br><span class="line"><span class="type">static</span> <span class="type">int</span> input_index = <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Uart1_ReceiveByte</span><span class="params">(u8 *Data)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 简单的串口初始化函数</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">Uart1_Simple_Init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    XUartPs_Config *UartConfig;</span><br><span class="line">    <span class="type">int</span> Status;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查找串口配置</span></span><br><span class="line">    UartConfig = XUartPs_LookupConfig(UART1_DEVICE_ID);</span><br><span class="line">    <span class="keyword">if</span> (UartConfig == <span class="literal">NULL</span>) {</span><br><span class="line">        xil_printf(<span class="string">"UART1 LookupConfig Failed!\r\n"</span>);</span><br><span class="line">        <span class="keyword">return</span> XST_FAILURE;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化串口</span></span><br><span class="line">    Status = XUartPs_CfgInitialize(&amp;Uart1_Inst, UartConfig, UartConfig-&gt;BaseAddress);</span><br><span class="line">    <span class="keyword">if</span> (Status != XST_SUCCESS) {</span><br><span class="line">        xil_printf(<span class="string">"UART1 CfgInitialize Failed!\r\n"</span>);</span><br><span class="line">        <span class="keyword">return</span> XST_FAILURE;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置波特率为115200</span></span><br><span class="line">    XUartPs_SetBaudRate(&amp;Uart1_Inst, <span class="number">115200</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置正常操作模式</span></span><br><span class="line">    XUartPs_SetOperMode(&amp;Uart1_Inst, XUARTPS_OPER_MODE_NORMAL);</span><br><span class="line"></span><br><span class="line">    xil_printf(<span class="string">"UART1 Simple Initialization Successful!\r\n"</span>);</span><br><span class="line">    <span class="keyword">return</span> XST_SUCCESS;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 轮询方式接收一个字符（非阻塞）</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">Uart1_ReceiveByte</span><span class="params">(u8 *Data)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">// 检查是否有数据可读</span></span><br><span class="line">    <span class="keyword">if</span> (XUartPs_IsReceiveData(Uart1_Inst.Config.BaseAddress)) {</span><br><span class="line">        *Data = XUartPs_ReadReg(Uart1_Inst.Config.BaseAddress, XUARTPS_FIFO_OFFSET);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>; <span class="comment">// 成功接收到数据</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">// 没有数据</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 发送字符串到串口</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Uart1_SendString</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> len = <span class="built_in">strlen</span>(str);</span><br><span class="line">    <span class="type">int</span> sent = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (sent &lt; len) {</span><br><span class="line">        sent += XUartPs_Send(&amp;Uart1_Inst, (u8*)&amp;str[sent], len - sent);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 简单的回显处理函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Uart1_EchoHandler</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    u8 ReceivedData;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 尝试接收一个字节</span></span><br><span class="line">    <span class="keyword">if</span> (Uart1_ReceiveByte(&amp;ReceivedData)) {</span><br><span class="line">        <span class="comment">// 回显字符</span></span><br><span class="line">        XUartPs_SendByte(Uart1_Inst.Config.BaseAddress, ReceivedData);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 处理回车键</span></span><br><span class="line">        <span class="keyword">if</span> (ReceivedData == <span class="string">'\r'</span> || ReceivedData == <span class="string">'\n'</span>) {</span><br><span class="line">            <span class="keyword">if</span> (input_index &gt; <span class="number">0</span>) {</span><br><span class="line">                input_buffer[input_index] = <span class="string">'\0'</span>; <span class="comment">// 添加字符串结束符</span></span><br><span class="line"></span><br><span class="line">                <span class="comment">// 换行</span></span><br><span class="line">                Uart1_SendString(<span class="string">"\r\n"</span>);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 处理特殊命令</span></span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer, <span class="string">"exit"</span>) == <span class="number">0</span>) {</span><br><span class="line">                    Uart1_SendString(<span class="string">"Exiting MicroPython REPL...\r\n"</span>);</span><br><span class="line">                    input_index = <span class="number">0</span>;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                }</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer, <span class="string">"help"</span>) == <span class="number">0</span>) {</span><br><span class="line">                    Uart1_SendString(<span class="string">"MicroPython REPL Commands:\r\n"</span>);</span><br><span class="line">                    Uart1_SendString(<span class="string">"  help - Show this help message\r\n"</span>);</span><br><span class="line">                    Uart1_SendString(<span class="string">"  exit - Exit REPL mode\r\n"</span>);</span><br><span class="line">                    Uart1_SendString(<span class="string">"  Enter Python code to execute\r\n"</span>);</span><br><span class="line">                }</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strlen</span>(input_buffer) &gt; <span class="number">0</span>) {</span><br><span class="line">                    <span class="comment">// 执行Python代码</span></span><br><span class="line">                    do_str(input_buffer, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                input_index = <span class="number">0</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 显示提示符</span></span><br><span class="line">            Uart1_SendString(<span class="string">"&gt;&gt;&gt; "</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">// 处理退格键</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (ReceivedData == <span class="number">0x7F</span> || ReceivedData == <span class="number">0x08</span>) { <span class="comment">// Backspace or Delete</span></span><br><span class="line">            <span class="keyword">if</span> (input_index &gt; <span class="number">0</span>) {</span><br><span class="line">                input_index--;</span><br><span class="line">                <span class="comment">// 在终端上显示退格效果</span></span><br><span class="line">                Uart1_SendString(<span class="string">"\b \b"</span>);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">// 处理普通字符</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (ReceivedData &gt;= <span class="number">0x20</span> &amp;&amp; ReceivedData &lt;= <span class="number">0x7E</span>) { <span class="comment">// 可打印字符</span></span><br><span class="line">            <span class="keyword">if</span> (input_index &lt; INPUT_BUFFER_SIZE - <span class="number">1</span>) {</span><br><span class="line">                input_buffer[input_index++] = ReceivedData;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">init_platform();</span><br><span class="line">py_init();<span class="comment">//初始化python</span></span><br><span class="line">    xil_printf(<span class="string">"ZYNQ MicroPython REPL Starting...\r\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> status;</span><br><span class="line">    status = XGpio_Initialize(&amp;LEDInst, LED_DEVICE_ID);</span><br><span class="line">    <span class="keyword">if</span>(status != XST_SUCCESS)</span><br><span class="line">        <span class="keyword">return</span> XST_FAILURE;</span><br><span class="line">    XGpio_SetDataDirection(&amp;LEDInst, <span class="number">1</span>, <span class="number">0</span>);     <span class="comment">// 设置GPIO方向为输出</span></span><br><span class="line"></span><br><span class="line">    XGpio_DiscreteWrite(&amp;LEDInst, <span class="number">1</span>, <span class="number">0b000</span>);<span class="comment">//测试LED</span></span><br><span class="line"></span><br><span class="line">    print(<span class="string">"Hello World\n\r"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试1: 基本输出和字符串操作</span></span><br><span class="line">    do_str(<span class="string">"print('=== MicroPython Basic Test ===')"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('Hello, ZYNQ!')"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试2: 创建和操作数组（列表）</span></span><br><span class="line">    do_str(<span class="string">"arr = [1, 2, 3, 4, 5]"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('Array:', arr)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"arr.append(6)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('After append:', arr)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('Array length:', len(arr))"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试3: 算术运算</span></span><br><span class="line">    do_str(<span class="string">"a, b = 10, 3"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('10 + 3 =', a + b)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('10 - 3 =', a - b)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('10 * 3 =', a * b)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('10 // 3 =', a // b)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试4: 逻辑判断和条件语句</span></span><br><span class="line">    do_str(<span class="string">"x = 15"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"if x &gt; 10: print('x is greater than 10')"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"if x % 2 == 0: print('x is even')"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"else: print('x is odd')"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试5: 循环操作</span></span><br><span class="line">    do_str(<span class="string">"print('Counting from 0 to 4:')"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"for i in range(5): print('Number:', i)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试6: 列表推导式（Python特色功能）</span></span><br><span class="line">    do_str(<span class="string">"squares = [i**2 for i in range(6)]"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('Squares of 0-5:', squares)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试7: 字典操作</span></span><br><span class="line">    do_str(<span class="string">"student = {'name': 'ZYNQ', 'score': 95}"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('Student info:', student)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('Student name:', student['name'])"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试8: 函数定义和调用</span></span><br><span class="line">    do_str(<span class="string">"def multiply(x, y): return x * y"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"result = multiply(7, 8)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line">    do_str(<span class="string">"print('7 * 8 =', result)"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line">    do_str(<span class="string">"print('=== All tests completed! ===')"</span>, MP_PARSE_SINGLE_INPUT);</span><br><span class="line"></span><br><span class="line"><span class="comment">//    while(1) //注释主循环，则会进入REPL交互模式</span></span><br><span class="line"><span class="comment">//    {</span></span><br><span class="line"><span class="comment">//        // 主循环</span></span><br><span class="line"><span class="comment">//    }</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化串口1</span></span><br><span class="line">    <span class="keyword">if</span> (Uart1_Simple_Init() != XST_SUCCESS) {</span><br><span class="line">        xil_printf(<span class="string">"UART1 Init Failed! System Halted.\r\n"</span>);</span><br><span class="line">        <span class="keyword">while</span>(<span class="number">1</span>); <span class="comment">// 初始化失败，停止系统</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    Uart1_SendString(<span class="string">"========================================\r\n"</span>);</span><br><span class="line">    Uart1_SendString(<span class="string">"    ZYNQ MicroPython REPL Environment\r\n"</span>);</span><br><span class="line">    Uart1_SendString(<span class="string">"========================================\r\n"</span>);</span><br><span class="line">    Uart1_SendString(<span class="string">"Type 'help' for available commands.\r\n"</span>);</span><br><span class="line">    Uart1_SendString(<span class="string">"Type Python code to execute immediately.\r\n"</span>);</span><br><span class="line">    Uart1_SendString(<span class="string">"&gt;&gt;&gt; "</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主循环 - REPL交互模式</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">        Uart1_EchoHandler();</span><br><span class="line">        <span class="comment">// 可以在这里添加其他任务或微小延迟</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    py_uinit();</span><br><span class="line">    cleanup_platform();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>运行成功后串口会返回python执行的结果，然后大家可以注释掉代码中间的while循环(上述代码已经注释了)，然后就可以通过串口调试助手，主动发送python代码执行(交互式，以回车结尾)，同时也可以来测试之前写的内存回收是否正确，如图</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/ZYNQ/zynq00000009.webp" width="500"><p>到此，基础功能移植结束，下面是GPIO输入输出，后期的GPIO测试依旧是使用这个主函数的交互功能</p><hr><h3 id="C-5-添加GPIO功能"><a href="#C-5-添加GPIO功能" class="headerlink" title="C.5 添加GPIO功能"></a>C.5 添加GPIO功能</h3><p>之前我们只是简单定义了<code>typedef struct _machine_pin_obj_t mp_hal_pin_obj_t;</code>,这里我们要根据自己的硬件来实现_machine_pin_obj_t，以及相关接口</p><p>注意这里我使用的是<code>AXI GPIO</code>，而非PS端的EMIO/MIO</p><p>为了区别GPIO功能，我们在bare-arm下面创建四个文件，专门用来实现GPIO功能<br>分别是： </p><ul><li><strong>machine_pin.c</strong></li><li><strong>machine_pin.h</strong></li><li><strong>modmachine.c</strong></li><li><strong>qstrdefs_port.h</strong></li></ul><p>前两个是具体驱动接口实现，第三个设备注册，最后一个是QSTR生成需要使用，<strong>注意，这四个文件和最开写配置文件一样，需要再linux环境下添加，然后使用make指令，生成新的bulid文件，然后再导入到SDK中开发才能正常通过编译</strong>,当然，bare-arm下面的Mkaefile文件也需要修改<br>同时也要修改删除mphalport.h 和mphalport.c中重复的部分，最后添加引脚映射以及基本的输入输出，暂时不包含中断等<br>下面给出修改后的文件参考：</p><p>这两个文件(machine_pin.c/h)主要是定义GPIO输入输出的接口，后期如果有新的按键LED等可以在这里添加修改，当然最好还是写一个映射表，我这里就写的比较简单，大家make的时候需要先把里面有关AXI GPIO的接口先注释掉，空实现即可，同时.h文件里面的宏也要暂时注释，否则编译会报错</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> MICROPY_INCLUDED_BARE_ARM_MACHINE_PIN_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_INCLUDED_BARE_ARM_MACHINE_PIN_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/obj.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MACHINE_PIN_MODE_IN   (0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MACHINE_PIN_MODE_OUT  (1)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LED_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//这里简单给出映射表：其中mode 1表示输入，0表示输出</span></span><br><span class="line"><span class="comment">/* pin_numbernamelocalID</span></span><br><span class="line"><span class="comment"> * 0led0Y14LED_DEVICE_ID</span></span><br><span class="line"><span class="comment"> * 1led1W14LED_DEVICE_ID</span></span><br><span class="line"><span class="comment"> * 2led2Y16LED_DEVICE_ID</span></span><br><span class="line"><span class="comment"> * 自行添加</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">machine_pin_obj_t</span> {</span></span><br><span class="line">    <span class="type">mp_obj_base_t</span> base;</span><br><span class="line">    <span class="type">uint32_t</span> pin_id;</span><br><span class="line">    <span class="type">uint32_t</span> mode;</span><br><span class="line">} <span class="type">machine_pin_obj_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Pin 类型 */</span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">const</span> <span class="type">mp_obj_type_t</span> machine_pin_type;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* GPIO HAL */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_gpio_init</span><span class="params">(<span class="type">uint32_t</span> pin, <span class="type">uint32_t</span> mode)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_gpio_write</span><span class="params">(<span class="type">uint32_t</span> pin, <span class="type">int</span> value)</span>;</span><br><span class="line"><span class="type">int</span>  <span class="title function_">mp_hal_gpio_read</span><span class="params">(<span class="type">uint32_t</span> pin)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/obj.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"machine_pin.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xparameters.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xuartps.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xuartps_hw.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xgpio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span>  <span class="comment">// for sleep()</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xil_printf.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> XGpio LEDInst;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ===================== Pin 构造函数 ===================== */</span></span><br><span class="line"><span class="type">static</span> <span class="type">mp_obj_t</span> <span class="title function_">machine_pin_make_new</span><span class="params">(<span class="type">const</span> <span class="type">mp_obj_type_t</span> *type,</span></span><br><span class="line"><span class="params">                                     <span class="type">size_t</span> n_args,</span></span><br><span class="line"><span class="params">                                     <span class="type">size_t</span> n_kw,</span></span><br><span class="line"><span class="params">                                     <span class="type">const</span> <span class="type">mp_obj_t</span> *args)</span> {</span><br><span class="line">    mp_arg_check_num(n_args, n_kw, <span class="number">2</span>, <span class="number">2</span>, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">machine_pin_obj_t</span> *self = m_new_obj(<span class="type">machine_pin_obj_t</span>);</span><br><span class="line">    self-&gt;base.type = type;</span><br><span class="line"></span><br><span class="line">    self-&gt;pin_id = mp_obj_get_int(args[<span class="number">0</span>]);</span><br><span class="line">    self-&gt;mode   = mp_obj_get_int(args[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* GPIO 初始化（需要自己实现） */</span></span><br><span class="line">    mp_hal_gpio_init(self-&gt;pin_id, self-&gt;mode);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> MP_OBJ_FROM_PTR(self);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ===================== Pin.value() ===================== */</span></span><br><span class="line"><span class="type">static</span> <span class="type">mp_obj_t</span> <span class="title function_">machine_pin_value</span><span class="params">(<span class="type">size_t</span> n_args, <span class="type">const</span> <span class="type">mp_obj_t</span> *args)</span> {</span><br><span class="line">    <span class="type">machine_pin_obj_t</span> *self = MP_OBJ_TO_PTR(args[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (n_args == <span class="number">1</span>) {</span><br><span class="line">        <span class="type">int</span> val = mp_hal_gpio_read(self-&gt;pin_id);</span><br><span class="line">        <span class="keyword">return</span> mp_obj_new_int(val);</span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        <span class="type">int</span> val = mp_obj_get_int(args[<span class="number">1</span>]);</span><br><span class="line">        mp_hal_gpio_write(self-&gt;pin_id, val);</span><br><span class="line">        <span class="keyword">return</span> mp_const_none;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="type">static</span> <span class="title function_">MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN</span><span class="params">(</span></span><br><span class="line"><span class="params">    machine_pin_value_obj, <span class="number">1</span>, <span class="number">2</span>, machine_pin_value</span></span><br><span class="line"><span class="params">)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ===================== locals_dict ===================== */</span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">mp_rom_map_elem_t</span> machine_pin_locals_dict_table[] = {</span><br><span class="line">    { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&amp;machine_pin_value_obj) },</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 类常量 */</span></span><br><span class="line">    { MP_ROM_QSTR(MP_QSTR_IN),  MP_ROM_INT(MACHINE_PIN_MODE_IN) },</span><br><span class="line">    { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(MACHINE_PIN_MODE_OUT) },</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="title function_">MP_DEFINE_CONST_DICT</span><span class="params">(</span></span><br><span class="line"><span class="params">    machine_pin_locals_dict,</span></span><br><span class="line"><span class="params">    machine_pin_locals_dict_table</span></span><br><span class="line"><span class="params">)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ===================== Pin 类型定义 ===================== */</span></span><br><span class="line"></span><br><span class="line">MP_DEFINE_CONST_OBJ_TYPE(</span><br><span class="line">    machine_pin_type,</span><br><span class="line">    MP_QSTR_Pin,</span><br><span class="line">    MP_TYPE_FLAG_NONE,</span><br><span class="line">    make_new, machine_pin_make_new,</span><br><span class="line">    locals_dict, &amp;machine_pin_locals_dict</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_gpio_init</span><span class="params">(<span class="type">uint32_t</span> pin, <span class="type">uint32_t</span> mode)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> status;</span><br><span class="line"><span class="keyword">if</span>(pin ==<span class="number">0</span> || pin==<span class="number">1</span> || pin ==<span class="number">2</span>)</span><br><span class="line">{</span><br><span class="line">status = XGpio_Initialize(&amp;LEDInst, LED_DEVICE_ID);</span><br><span class="line"><span class="keyword">if</span>(status != XST_SUCCESS)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"mp_hal_gpio_init error!!!!\r\n"</span>);</span><br><span class="line">XGpio_SetDataDirection(&amp;LEDInst, <span class="number">1</span>, mode);     <span class="comment">// 设置GPIO方向为输出/输入</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_gpio_write</span><span class="params">(<span class="type">uint32_t</span> pin, <span class="type">int</span> value)</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span>(pin ==<span class="number">0</span> || pin==<span class="number">1</span> || pin ==<span class="number">2</span>)</span><br><span class="line">{</span><br><span class="line"><span class="comment">// 使用位运算生成对应引脚的掩码</span></span><br><span class="line"><span class="type">uint32_t</span> pin_mask = (<span class="number">1</span> &lt;&lt; pin);  <span class="comment">// 将引脚号转换为位掩码</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (value) {</span><br><span class="line">XGpio_DiscreteSet(&amp;LEDInst, <span class="number">1</span>, pin_mask);  <span class="comment">// 设置对应位为高电平</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line">XGpio_DiscreteClear(&amp;LEDInst, <span class="number">1</span>, pin_mask);  <span class="comment">// 清除对应位为低电平</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_gpio_read</span><span class="params">(<span class="type">uint32_t</span> pin)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(pin == <span class="number">0</span> || pin == <span class="number">1</span> || pin == <span class="number">2</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">// 读取整个GPIO通道的状态</span></span><br><span class="line">        <span class="type">uint32_t</span> gpio_state = XGpio_DiscreteRead(&amp;LEDInst, <span class="number">1</span>);</span><br><span class="line">        <span class="comment">// 使用位掩码提取特定引脚的状态</span></span><br><span class="line">        <span class="type">uint32_t</span> pin_mask = (<span class="number">1</span> &lt;&lt; pin);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回该引脚的状态 (0 或 1)</span></span><br><span class="line">        <span class="keyword">return</span> (gpio_state &amp; pin_mask) ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>; <span class="comment">// 引脚号错误返回-1</span></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>下面这个文件就是对machine模块进行注册的主体，参考官方模版</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// machine.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/obj.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"machine_pin.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"mpconfigport.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* ===================== Pin 类型声明 ===================== */</span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">const</span> <span class="type">mp_obj_type_t</span> machine_pin_type;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ===================== machine 模块 globals ===================== */</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">mp_rom_map_elem_t</span> machine_module_globals_table[] = {</span><br><span class="line">    { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&amp;machine_pin_type) },</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//这里我们我直接写到objmodule.c文件中，所以这里不列出数组</span></span><br><span class="line"><span class="comment">//static const mp_rom_map_elem_t mp_builtin_module_table[] = {</span></span><br><span class="line"><span class="comment">//    MICROPY_PORT_BUILTIN_MODULES</span></span><br><span class="line"><span class="comment">//    // ... 其他模块</span></span><br><span class="line"><span class="comment">//};</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="title function_">MP_DEFINE_CONST_DICT</span><span class="params">(machine_module_globals, machine_module_globals_table)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ===================== machine 模块对象 ===================== */</span></span><br><span class="line"><span class="type">const</span> <span class="type">mp_obj_module_t</span> machine_module = {</span><br><span class="line">    .base = { &amp;mp_type_module },</span><br><span class="line">    .globals = (<span class="type">mp_obj_dict_t</span>*)&amp;machine_module_globals,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//我们手动注册</span></span><br><span class="line"><span class="comment">/* ===================== 注册模块 ===================== */</span></span><br><span class="line"><span class="comment">//MP_REGISTER_MODULE(MP_QSTR_machine, machine_module);</span></span><br></pre></td></tr></table></figure></div><p>这个文件主要是添加我们这次注册需要使用的QSTR，make的时候会帮我们把字符串更新到之前的那个qstrdefs.generated.h文件中</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// qstrdefs_port.h</span></span><br><span class="line">Q(Pin)</span><br><span class="line">Q(IN)</span><br><span class="line">Q(OUT)</span><br><span class="line">Q(value)</span><br><span class="line">Q(machine)  <span class="comment">// 注意这里定义模块名</span></span><br></pre></td></tr></table></figure></div><p>这里是修改后的mphalport.h/c文件，因为我们GPIO独立出来了</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在 mphalport.h 或相应移植文件中实现</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"mphalport.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/mphal.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/gc.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/mperrno.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xparameters.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xgpio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xstatus.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="type">int</span> <span class="title function_">Uart1_ReceiveByte</span><span class="params">(u8 *Data)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 时间控制函数</span></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_ms</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回系统启动后的毫秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_us</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回系统启动后的微秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_cpu</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回CPU时钟计数（用于高精度计时）</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_ms</span><span class="params">(<span class="type">mp_uint_t</span> ms)</span> {</span><br><span class="line">    <span class="comment">// 阻塞延时毫秒</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_us</span><span class="params">(<span class="type">mp_uint_t</span> us)</span> {</span><br><span class="line">    <span class="comment">// 阻塞延时微秒</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标准输入输出函数</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_stdin_rx_chr</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    u8 ReceivedData;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 尝试接收一个字节</span></span><br><span class="line">    <span class="keyword">if</span> (Uart1_ReceiveByte(&amp;ReceivedData)) {</span><br><span class="line">        <span class="comment">// 接收到数据，直接回显</span></span><br><span class="line">        <span class="comment">//XUartPs_SendByte(Uart1_Inst.Config.BaseAddress, ReceivedData);</span></span><br><span class="line">    <span class="keyword">return</span> ReceivedData;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_str</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str)</span> {</span><br><span class="line">    <span class="comment">// 向标准输出发送字符串</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%.*s"</span>, <span class="built_in">strlen</span>(str), str);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_stdout_tx_strn</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span> {</span><br><span class="line">    <span class="comment">// 向标准输出发送指定长度字符串</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%.*s"</span>, len, str);</span><br><span class="line">    <span class="keyword">return</span> len;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_strn_cooked</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%.*s"</span>, len, str);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">uintptr_t</span> <span class="title function_">mp_hal_stdio_poll</span><span class="params">(<span class="type">uintptr_t</span> poll_flags)</span> {</span><br><span class="line">    <span class="comment">// 检查标准输入/输出状态（用于非阻塞操作）</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 系统控制函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_set_interrupt_char</span><span class="params">(<span class="type">char</span> c)</span> {</span><br><span class="line">    <span class="comment">// 设置中断字符（如Ctrl+C）</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint64_t</span> <span class="title function_">mp_hal_time_ns</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">    <span class="comment">// 返回自纪元以来的纳秒数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __MPHALPORT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __MPHALPORT_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/mpconfig.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/obj.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"xgpio.h"</span>  <span class="comment">// 添加Xilinx GPIO支持</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 扩展的引脚对象结构体,这里暂时没用上</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">mp_hal_pin_obj_t</span> {</span></span><br><span class="line">    <span class="type">mp_obj_base_t</span> base;      <span class="comment">// MicroPython 对象基类</span></span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> *name;        <span class="comment">// 引脚名称</span></span><br><span class="line">    <span class="type">uint8_t</span> bank;            <span class="comment">// GPIO Bank</span></span><br><span class="line">    <span class="type">uint8_t</span> pin_num;         <span class="comment">// 引脚编号</span></span><br><span class="line">    <span class="type">uint8_t</span> channel;         <span class="comment">// 通道</span></span><br><span class="line">    <span class="type">uint8_t</span> bit_mask;        <span class="comment">// 位掩码，用于区分LED</span></span><br><span class="line">} <span class="type">mouse_mp_hal_pin_obj_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// GPIO引脚操作函数（自定义GPIO API）</span></span><br><span class="line"><span class="comment">// 引脚对象类型定义</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">machine_pin_obj_t</span> <span class="title">mp_hal_pin_obj_t</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 时间控制函数</span></span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_ms</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_us</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_ticks_cpu</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_ms</span><span class="params">(<span class="type">mp_uint_t</span> ms)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_delay_us</span><span class="params">(<span class="type">mp_uint_t</span> us)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标准输入输出函数</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mp_hal_stdin_rx_chr</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_str</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str)</span>;</span><br><span class="line"><span class="type">mp_uint_t</span> <span class="title function_">mp_hal_stdout_tx_strn</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_stdout_tx_strn_cooked</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">size_t</span> len)</span>;</span><br><span class="line"><span class="type">uintptr_t</span> <span class="title function_">mp_hal_stdio_poll</span><span class="params">(<span class="type">uintptr_t</span> poll_flags)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 系统控制函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_hal_set_interrupt_char</span><span class="params">(<span class="type">char</span> c)</span>;</span><br><span class="line"><span class="type">uint64_t</span> <span class="title function_">mp_hal_time_ns</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>这里要修改makefile，增加我们新创建的文件，否则我们添加的文件都没办法正常编译，添加在SRC_C之后，下面还新加一个Qstr的头文件定义，注意这里的文件名不要写错了，要和我们编译的文件名一致</p><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Include the core environment definitions; this will set $(TOP).</span></span><br><span class="line"><span class="keyword">include</span> ../../py/mkenv.mk</span><br><span class="line"></span><br><span class="line"><span class="comment"># Include py core make definitions.</span></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(TOP)</span>/py/py.mk</span><br><span class="line"></span><br><span class="line"><span class="comment"># Set makefile-level MicroPython feature configurations.</span></span><br><span class="line">MICROPY_ROM_TEXT_COMPRESSION ?= 0</span><br><span class="line"></span><br><span class="line"><span class="comment"># Define toolchain and other tools.</span></span><br><span class="line">CROSS_COMPILE ?= arm-none-eabi-</span><br><span class="line">DFU ?= <span class="variable">$(TOP)</span>/tools/dfu.py</span><br><span class="line">PYDFU ?= <span class="variable">$(TOP)</span>/tools/pydfu.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># Set CFLAGS.</span></span><br><span class="line">CFLAGS += -I. -I<span class="variable">$(TOP)</span> -I<span class="variable">$(BUILD)</span></span><br><span class="line">CFLAGS += -Wall -Werror -std=c99 -nostdlib</span><br><span class="line">CFLAGS += -mthumb -mtune=cortex-m4 -mcpu=cortex-m4 -msoft-float</span><br><span class="line">CSUPEROPT = -Os <span class="comment"># save some code space for performance-critical code</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Select debugging or optimisation build.</span></span><br><span class="line"><span class="keyword">ifeq</span> (<span class="variable">$(DEBUG)</span>, 1)</span><br><span class="line">CFLAGS += -Og</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">CFLAGS += -Os -DNDEBUG</span><br><span class="line">CFLAGS += -fdata-sections -ffunction-sections</span><br><span class="line"><span class="keyword">endif</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Set linker flags.</span></span><br><span class="line">LDFLAGS += -nostdlib -T stm32f405.ld --gc-sections</span><br><span class="line"></span><br><span class="line"><span class="comment"># Define the required source files.</span></span><br><span class="line">SRC_C += lib.c main.c system.c modmachine.c machine_pin.c  </span><br><span class="line"></span><br><span class="line">QSTR_DEFS += qstrdefs_port.h</span><br><span class="line"></span><br><span class="line"><span class="comment"># Define the required object files.</span></span><br><span class="line">OBJ += <span class="variable">$(PY_CORE_O)</span></span><br><span class="line">OBJ += <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(BUILD)</span>/, $(SRC_C:.c=.o)</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Define the top-level target, the main firmware.</span></span><br><span class="line"><span class="section">all: <span class="variable">$(BUILD)</span>/firmware.dfu</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD)</span>/firmware.elf: <span class="variable">$(OBJ)</span></span><br><span class="line"><span class="variable">$(ECHO)</span> <span class="string">"LINK <span class="variable">$@</span>"</span></span><br><span class="line"><span class="variable">$(Q)</span><span class="variable">$(LD)</span> <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$^</span></span><br><span class="line"><span class="variable">$(Q)</span><span class="variable">$(SIZE)</span> <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD)</span>/firmware.bin: <span class="variable">$(BUILD)</span>/firmware.elf</span><br><span class="line"><span class="variable">$(ECHO)</span> <span class="string">"Create <span class="variable">$@</span>"</span></span><br><span class="line"><span class="variable">$(Q)</span><span class="variable">$(OBJCOPY)</span> -O binary -j .isr_vector -j .text -j .data <span class="variable">$^</span> <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD)</span>/firmware.dfu: <span class="variable">$(BUILD)</span>/firmware.bin</span><br><span class="line"><span class="variable">$(ECHO)</span> <span class="string">"Create <span class="variable">$@</span>"</span></span><br><span class="line"><span class="variable">$(Q)</span><span class="variable">$(PYTHON)</span> <span class="variable">$(DFU)</span> -b 0x08000000:<span class="variable">$^</span> <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="section">deploy: <span class="variable">$(BUILD)</span>/firmware.dfu</span></span><br><span class="line"><span class="variable">$(Q)</span><span class="variable">$(PYTHON)</span> <span class="variable">$(PYDFU)</span> -u <span class="variable">$^</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Include remaining core make rules.</span></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(TOP)</span>/py/mkrules.mk</span><br></pre></td></tr></table></figure></div><p>最后就是注册模块，我们放在配置文件底部</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This file is part of the MicroPython project, http://micropython.org/</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The MIT License (MIT)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Copyright (c) 2014-2021 Damien P. George</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Permission is hereby granted, free of charge, to any person obtaining a copy</span></span><br><span class="line"><span class="comment"> * of this software and associated documentation files (the "Software"), to deal</span></span><br><span class="line"><span class="comment"> * in the Software without restriction, including without limitation the rights</span></span><br><span class="line"><span class="comment"> * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span></span><br><span class="line"><span class="comment"> * copies of the Software, and to permit persons to whom the Software is</span></span><br><span class="line"><span class="comment"> * furnished to do so, subject to the following conditions:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The above copyright notice and this permission notice shall be included in</span></span><br><span class="line"><span class="comment"> * all copies or substantial portions of the Software.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span></span><br><span class="line"><span class="comment"> * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span></span><br><span class="line"><span class="comment"> * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</span></span><br><span class="line"><span class="comment"> * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</span></span><br><span class="line"><span class="comment"> * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</span></span><br><span class="line"><span class="comment"> * OUT OF OR IN CONNECTION WITH THE USE OR OTHER DEALINGS IN</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭 micropython 内置模块（避免 extmod 依赖）</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MICROPYTHON (0) <span class="comment">// 禁用 micropython 模块</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_BUILTINS    (1) <span class="comment">// 启用内置函数和异常</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_GC          (1) <span class="comment">// 启用垃圾回收模块</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_ENABLE_GC (1)      <span class="comment">// 启用垃圾回收功能，否则变量功能无法使用</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制 MicroPython 构建方式的选项</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用最小化的起始配置（禁用所有可选功能）</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_CONFIG_ROM_LEVEL                (MICROPY_CONFIG_ROM_LEVEL_MINIMUM)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 编译器配置</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_ENABLE_COMPILER                 (1) <span class="comment">// 启用编译器</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Python 内部特性</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) <span class="comment">// 详细错误报告</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_CPYTHON_COMPAT (0) <span class="comment">// 不与 CPython 严格兼容以节省空间</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_DEBUG_PRINTERS (1) <span class="comment">// 启用调试打印机</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 精细控制 Python 内置功能、类、模块等</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_SYS                          (0) <span class="comment">// 禁用 sys 模块</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 为 GPIO 控制添加的配置</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE                      (1) <span class="comment">// 启用 machine 模块（包含GPIO控制）</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE_PIN_MAKE_NEW         (1) <span class="comment">// 启用创建新 Pin 对象的功能</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE_PIN_READ_METHOD      (1) <span class="comment">// 启用引脚读取方法</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PY_MACHINE_PIN_WRITE_METHOD     (1) <span class="comment">// 启用引脚写入方法</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 特定机器的类型定义</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">long</span> <span class="type">mp_off_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 需要提供 alloca() 函数的声明/定义</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;alloca.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 在 mpconfigport.h 文件中</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用不完整类型（前置声明）来声明 machine_module，避免包含 py/obj.h，否则会报错，我也不知道什么原因，先能跑再说</span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">mp_obj_module_t</span> <span class="title">machine_module</span>;</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MICROPY_PORT_BUILTIN_MODULES \</span></span><br><span class="line"><span class="meta">    { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&amp;machine_module) },</span></span><br></pre></td></tr></table></figure></div><p>这里遇到的问题是没有办法自动注册模块，因为我发现编译没办法生成我需要的moduleefs.h文件，所以这里我选择直接打开objmodule.c文件，然后在注册使用的数组mp_builtin_module_table里面主动添加我使用的模块名称，然后将模块名称定义在上面的mpconfigport.h文件最底部</p><p>注意: <strong>当修改mp_builtin_module_table数组之后，重新编译生成QSTR的时候可能会报错，这个时候暂时将添加的宏注释掉(不影响编译结果)，可能是文件头文件包含的问题，等具体使用的时候再添加即可</strong></p><p>这里简单修改objmodule.c文件，其实只是在数组后面添加了一个宏</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This file is part of the MicroPython project, http://micropython.org/</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The MIT License (MIT)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Copyright (c) 2013-2019 Damien P. George</span></span><br><span class="line"><span class="comment"> * Copyright (c) 2014-2015 Paul Sokolovsky</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Permission is hereby granted, free of charge, to any person obtaining a copy</span></span><br><span class="line"><span class="comment"> * of this software and associated documentation files (the "Software"), to deal</span></span><br><span class="line"><span class="comment"> * in the Software without restriction, including without limitation the rights</span></span><br><span class="line"><span class="comment"> * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span></span><br><span class="line"><span class="comment"> * copies of the Software, and to permit persons to whom the Software is</span></span><br><span class="line"><span class="comment"> * furnished to do so, subject to the following conditions:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The above copyright notice and this permission notice shall be included in</span></span><br><span class="line"><span class="comment"> * all copies or substantial portions of the Software.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span></span><br><span class="line"><span class="comment"> * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span></span><br><span class="line"><span class="comment"> * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</span></span><br><span class="line"><span class="comment"> * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</span></span><br><span class="line"><span class="comment"> * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</span></span><br><span class="line"><span class="comment"> * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN</span></span><br><span class="line"><span class="comment"> * THE SOFTWARE.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;assert.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/bc.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/objmodule.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/runtime.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"py/builtin.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">module_print</span><span class="params">(<span class="type">const</span> <span class="type">mp_print_t</span> *print, <span class="type">mp_obj_t</span> self_in, <span class="type">mp_print_kind_t</span> kind)</span> {</span><br><span class="line">    (<span class="type">void</span>)kind;</span><br><span class="line">    <span class="type">mp_obj_module_t</span> *self = MP_OBJ_TO_PTR(self_in);</span><br><span class="line"></span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> *module_name = <span class="string">""</span>;</span><br><span class="line">    <span class="type">mp_map_elem_t</span> *elem = mp_map_lookup(&amp;self-&gt;globals-&gt;<span class="built_in">map</span>, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_MAP_LOOKUP);</span><br><span class="line">    <span class="keyword">if</span> (elem != <span class="literal">NULL</span>) {</span><br><span class="line">        module_name = mp_obj_str_get_str(elem-&gt;value);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">#<span class="keyword">if</span> MICROPY_MODULE___FILE__</span></span><br><span class="line">    <span class="comment">// If we store __file__ to imported modules then try to lookup this</span></span><br><span class="line">    <span class="comment">// symbol to give more information about the module.</span></span><br><span class="line">    elem = mp_map_lookup(&amp;self-&gt;globals-&gt;<span class="built_in">map</span>, MP_OBJ_NEW_QSTR(MP_QSTR___file__), MP_MAP_LOOKUP);</span><br><span class="line">    <span class="keyword">if</span> (elem != <span class="literal">NULL</span>) {</span><br><span class="line">        mp_printf(print, <span class="string">"&lt;module '%s' from '%s'&gt;"</span>, module_name, mp_obj_str_get_str(elem-&gt;value));</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">    mp_printf(print, <span class="string">"&lt;module '%s'&gt;"</span>, module_name);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">module_attr_try_delegation</span><span class="params">(<span class="type">mp_obj_t</span> self_in, qstr attr, <span class="type">mp_obj_t</span> *dest)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">module_attr</span><span class="params">(<span class="type">mp_obj_t</span> self_in, qstr attr, <span class="type">mp_obj_t</span> *dest)</span> {</span><br><span class="line">    <span class="type">mp_obj_module_t</span> *self = MP_OBJ_TO_PTR(self_in);</span><br><span class="line">    <span class="keyword">if</span> (dest[<span class="number">0</span>] == MP_OBJ_NULL) {</span><br><span class="line">        <span class="comment">// load attribute</span></span><br><span class="line">        <span class="type">mp_map_elem_t</span> *elem = mp_map_lookup(&amp;self-&gt;globals-&gt;<span class="built_in">map</span>, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);</span><br><span class="line">        <span class="keyword">if</span> (elem != <span class="literal">NULL</span>) {</span><br><span class="line">            dest[<span class="number">0</span>] = elem-&gt;value;</span><br><span class="line">        <span class="meta">#<span class="keyword">if</span> MICROPY_CPYTHON_COMPAT</span></span><br><span class="line">        } <span class="keyword">else</span> <span class="keyword">if</span> (attr == MP_QSTR___dict__) {</span><br><span class="line">            dest[<span class="number">0</span>] = MP_OBJ_FROM_PTR(self-&gt;globals);</span><br><span class="line">        <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">        <span class="meta">#<span class="keyword">if</span> MICROPY_MODULE_GETATTR</span></span><br><span class="line">        } <span class="keyword">else</span> <span class="keyword">if</span> (attr != MP_QSTR___getattr__) {</span><br><span class="line">            elem = mp_map_lookup(&amp;self-&gt;globals-&gt;<span class="built_in">map</span>, MP_OBJ_NEW_QSTR(MP_QSTR___getattr__), MP_MAP_LOOKUP);</span><br><span class="line">            <span class="keyword">if</span> (elem != <span class="literal">NULL</span>) {</span><br><span class="line">                dest[<span class="number">0</span>] = mp_call_function_1(elem-&gt;value, MP_OBJ_NEW_QSTR(attr));</span><br><span class="line">            } <span class="keyword">else</span> {</span><br><span class="line">                module_attr_try_delegation(self_in, attr, dest);</span><br><span class="line">            }</span><br><span class="line">        <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            module_attr_try_delegation(self_in, attr, dest);</span><br><span class="line">        }</span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// delete/store attribute</span></span><br><span class="line">        <span class="type">mp_obj_dict_t</span> *dict = self-&gt;globals;</span><br><span class="line">        <span class="keyword">if</span> (dict-&gt;<span class="built_in">map</span>.is_fixed) {</span><br><span class="line">            <span class="meta">#<span class="keyword">if</span> MICROPY_CAN_OVERRIDE_BUILTINS</span></span><br><span class="line">            <span class="keyword">if</span> (dict == &amp;mp_module_builtins_globals) {</span><br><span class="line">                <span class="keyword">if</span> (MP_STATE_VM(mp_module_builtins_override_dict) == <span class="literal">NULL</span>) {</span><br><span class="line">                    MP_STATE_VM(mp_module_builtins_override_dict) = MP_OBJ_TO_PTR(mp_obj_new_dict(<span class="number">1</span>));</span><br><span class="line">                }</span><br><span class="line">                dict = MP_STATE_VM(mp_module_builtins_override_dict);</span><br><span class="line">            } <span class="keyword">else</span></span><br><span class="line">            <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// can't delete or store to fixed map</span></span><br><span class="line">                module_attr_try_delegation(self_in, attr, dest);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (dest[<span class="number">1</span>] == MP_OBJ_NULL) {</span><br><span class="line">            <span class="comment">// delete attribute</span></span><br><span class="line">            mp_obj_dict_delete(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(attr));</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            <span class="comment">// store attribute</span></span><br><span class="line">            mp_obj_dict_store(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(attr), dest[<span class="number">1</span>]);</span><br><span class="line">        }</span><br><span class="line">        dest[<span class="number">0</span>] = MP_OBJ_NULL; <span class="comment">// indicate success</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">MP_DEFINE_CONST_OBJ_TYPE(</span><br><span class="line">    mp_type_module,</span><br><span class="line">    MP_QSTR_module,</span><br><span class="line">    MP_TYPE_FLAG_NONE,</span><br><span class="line">    print, module_print,</span><br><span class="line">    attr, module_attr</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line"><span class="type">mp_obj_t</span> <span class="title function_">mp_obj_new_module</span><span class="params">(qstr module_name)</span> {</span><br><span class="line">    <span class="type">mp_map_t</span> *mp_loaded_modules_map = &amp;MP_STATE_VM(mp_loaded_modules_dict).<span class="built_in">map</span>;</span><br><span class="line">    <span class="type">mp_map_elem_t</span> *el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);</span><br><span class="line">    <span class="comment">// We could error out if module already exists, but let C extensions</span></span><br><span class="line">    <span class="comment">// add new members to existing modules.</span></span><br><span class="line">    <span class="keyword">if</span> (el-&gt;value != MP_OBJ_NULL) {</span><br><span class="line">        <span class="keyword">return</span> el-&gt;value;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// create new module object</span></span><br><span class="line">    <span class="type">mp_module_context_t</span> *o = m_new_obj(<span class="type">mp_module_context_t</span>);</span><br><span class="line">    o-&gt;module.base.type = &amp;mp_type_module;</span><br><span class="line">    o-&gt;module.globals = MP_OBJ_TO_PTR(mp_obj_new_dict(MICROPY_MODULE_DICT_SIZE));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// store __name__ entry in the module</span></span><br><span class="line">    mp_obj_dict_store(MP_OBJ_FROM_PTR(o-&gt;module.globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(module_name));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// store the new module into the slot in the global dict holding all modules</span></span><br><span class="line">    el-&gt;value = MP_OBJ_FROM_PTR(o);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// return the new module</span></span><br><span class="line">    <span class="keyword">return</span> MP_OBJ_FROM_PTR(o);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/******************************************************************************/</span></span><br><span class="line"><span class="comment">// Global module table and related functions</span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">mp_rom_map_elem_t</span> mp_builtin_module_table[] = {</span><br><span class="line">    <span class="comment">// built-in modules declared with MP_REGISTER_MODULE()</span></span><br><span class="line">    MICROPY_REGISTERED_MODULES \</span><br><span class="line">MICROPY_PORT_BUILTIN_MODULES\</span><br><span class="line">};</span><br><span class="line">MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table);</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">mp_rom_map_elem_t</span> mp_builtin_extensible_module_table[] = {</span><br><span class="line">    <span class="comment">// built-in modules declared with MP_REGISTER_EXTENSIBLE_MODULE()</span></span><br><span class="line">    MICROPY_REGISTERED_EXTENSIBLE_MODULES</span><br><span class="line">};</span><br><span class="line">MP_DEFINE_CONST_MAP(mp_builtin_extensible_module_map, mp_builtin_extensible_module_table);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> MICROPY_MODULE_ATTR_DELEGATION &amp;&amp; defined(MICROPY_MODULE_DELEGATIONS)</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">mp_module_delegation_entry_t</span> {</span></span><br><span class="line">    <span class="type">mp_rom_obj_t</span> mod;</span><br><span class="line">    <span class="type">mp_attr_fun_t</span> fun;</span><br><span class="line">} <span class="type">mp_module_delegation_entry_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">mp_module_delegation_entry_t</span> mp_builtin_module_delegation_table[] = {</span><br><span class="line">    <span class="comment">// delegation entries declared with MP_REGISTER_MODULE_DELEGATION()</span></span><br><span class="line">    MICROPY_MODULE_DELEGATIONS</span><br><span class="line">};</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Attempts to find (and initialise) a built-in, otherwise returns</span></span><br><span class="line"><span class="comment">// MP_OBJ_NULL.</span></span><br><span class="line"><span class="type">mp_obj_t</span> <span class="title function_">mp_module_get_builtin</span><span class="params">(qstr module_name, <span class="type">bool</span> extensible)</span> {</span><br><span class="line">    <span class="meta">#<span class="keyword">if</span> MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES</span></span><br><span class="line">    <span class="type">const</span> <span class="type">mp_map_t</span> *<span class="built_in">map</span> = extensible ? &amp;mp_builtin_extensible_module_map : &amp;mp_builtin_module_map;</span><br><span class="line">    <span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">    <span class="type">const</span> <span class="type">mp_map_t</span> *<span class="built_in">map</span> = &amp;mp_builtin_module_map;</span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    <span class="type">mp_map_elem_t</span> *elem = mp_map_lookup((<span class="type">mp_map_t</span> *)<span class="built_in">map</span>, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!elem) {</span><br><span class="line">        <span class="meta">#<span class="keyword">if</span> MICROPY_PY_SYS</span></span><br><span class="line">        <span class="comment">// Special case for sys, which isn't extensible but can always be</span></span><br><span class="line">        <span class="comment">// imported with the alias `usys`.</span></span><br><span class="line">        <span class="keyword">if</span> (module_name == MP_QSTR_usys) {</span><br><span class="line">            <span class="keyword">return</span> MP_OBJ_FROM_PTR(&amp;mp_module_sys);</span><br><span class="line">        }</span><br><span class="line">        <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">        <span class="meta">#<span class="keyword">if</span> MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES</span></span><br><span class="line">        <span class="keyword">if</span> (extensible) {</span><br><span class="line">            <span class="comment">// At this point we've already tried non-extensible built-ins, the</span></span><br><span class="line">            <span class="comment">// filesystem, and now extensible built-ins. No match, so fail</span></span><br><span class="line">            <span class="comment">// the import.</span></span><br><span class="line">            <span class="keyword">return</span> MP_OBJ_NULL;</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// We're trying to match a non-extensible built-in (i.e. before trying</span></span><br><span class="line">        <span class="comment">// the filesystem), but if the user is importing `ufoo`, _and_ `foo`</span></span><br><span class="line">        <span class="comment">// is an extensible module, then allow it as a way of forcing the</span></span><br><span class="line">        <span class="comment">// built-in. Essentially, this makes it as if all the extensible</span></span><br><span class="line">        <span class="comment">// built-ins also had non-extensible aliases named `ufoo`. Newer code</span></span><br><span class="line">        <span class="comment">// should be using sys.path to force the built-in, but this retains</span></span><br><span class="line">        <span class="comment">// the old behaviour of the u-prefix being used to force a built-in</span></span><br><span class="line">        <span class="comment">// import.</span></span><br><span class="line">        <span class="type">size_t</span> module_name_len;</span><br><span class="line">        <span class="type">const</span> <span class="type">char</span> *module_name_str = (<span class="type">const</span> <span class="type">char</span> *)qstr_data(module_name, &amp;module_name_len);</span><br><span class="line">        <span class="keyword">if</span> (module_name_str[<span class="number">0</span>] != <span class="string">'u'</span>) {</span><br><span class="line">            <span class="keyword">return</span> MP_OBJ_NULL;</span><br><span class="line">        }</span><br><span class="line">        elem = mp_map_lookup((<span class="type">mp_map_t</span> *)&amp;mp_builtin_extensible_module_map, MP_OBJ_NEW_QSTR(qstr_find_strn(module_name_str + <span class="number">1</span>, module_name_len - <span class="number">1</span>)), MP_MAP_LOOKUP);</span><br><span class="line">        <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!elem) {</span><br><span class="line">            <span class="keyword">return</span> MP_OBJ_NULL;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">#<span class="keyword">if</span> MICROPY_MODULE_BUILTIN_INIT</span></span><br><span class="line">    <span class="comment">// If found, it's a newly loaded built-in, so init it. This can run</span></span><br><span class="line">    <span class="comment">// multiple times, so the module must ensure that it handles being</span></span><br><span class="line">    <span class="comment">// initialised multiple times.</span></span><br><span class="line">    <span class="type">mp_obj_t</span> dest[<span class="number">2</span>];</span><br><span class="line">    mp_load_method_maybe(elem-&gt;value, MP_QSTR___init__, dest);</span><br><span class="line">    <span class="keyword">if</span> (dest[<span class="number">0</span>] != MP_OBJ_NULL) {</span><br><span class="line">        mp_call_method_n_kw(<span class="number">0</span>, <span class="number">0</span>, dest);</span><br><span class="line">    }</span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> elem-&gt;value;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">module_attr_try_delegation</span><span class="params">(<span class="type">mp_obj_t</span> self_in, qstr attr, <span class="type">mp_obj_t</span> *dest)</span> {</span><br><span class="line">    <span class="meta">#<span class="keyword">if</span> MICROPY_MODULE_ATTR_DELEGATION &amp;&amp; defined(MICROPY_MODULE_DELEGATIONS)</span></span><br><span class="line">    <span class="comment">// Delegate lookup to a module's custom attr method.</span></span><br><span class="line">    <span class="type">size_t</span> n = MP_ARRAY_SIZE(mp_builtin_module_delegation_table);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; n; ++i) {</span><br><span class="line">        <span class="keyword">if</span> (*(<span class="type">mp_obj_t</span> *)(&amp;mp_builtin_module_delegation_table[i].mod) == self_in) {</span><br><span class="line">            mp_builtin_module_delegation_table[i].fun(self_in, attr, dest);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">    (<span class="type">void</span>)self_in;</span><br><span class="line">    (<span class="type">void</span>)attr;</span><br><span class="line">    (<span class="type">void</span>)dest;</span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mp_module_generic_attr</span><span class="params">(qstr attr, <span class="type">mp_obj_t</span> *dest, <span class="type">const</span> <span class="type">uint16_t</span> *keys, <span class="type">mp_obj_t</span> *values)</span> {</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; keys[i] != MP_QSTRnull; ++i) {</span><br><span class="line">        <span class="keyword">if</span> (attr == keys[i]) {</span><br><span class="line">            <span class="keyword">if</span> (dest[<span class="number">0</span>] == MP_OBJ_NULL) {</span><br><span class="line">                <span class="comment">// load attribute (MP_OBJ_NULL returned for deleted items)</span></span><br><span class="line">                dest[<span class="number">0</span>] = values[i];</span><br><span class="line">            } <span class="keyword">else</span> {</span><br><span class="line">                <span class="comment">// delete or store (delete stores MP_OBJ_NULL)</span></span><br><span class="line">                values[i] = dest[<span class="number">1</span>];</span><br><span class="line">                dest[<span class="number">0</span>] = MP_OBJ_NULL; <span class="comment">// indicate success</span></span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>添加修改完上述文件之后，然后和之前一样make，生成新的build文件，我们将它添加到SDK中，然后再取消之前对接口的注释，编译成功之后直接烧录到板子中，注意注意，上述代码中有关ZYNQ的头文件，比如#include “xgpio.h”等，在make的时候都要注释掉，否则会报错找不到头文件，我因为是贴的SDK的最终代码，所以并没有注释或删除</p><hr><p>简单说说这里添加的内容，我们并没有实现之前<code>_mp_hal_pin_obj_t</code>结构体，而是创建一个machine模块(后期如果实现IIC等的可能会更方便一点，但其实我也还没试)，在里面添加我们需要的信息，然后建立一个映射表(这里我们使用的是if判断)，将python代码中的名称映射到具体硬件引脚上，方便python代码的书写，其次，因为ZYNQ当使用 XGpioPs_WritePin这类函数时，它操作的是整个GPIO端口的32位数据寄存器，如果想独立控制多个LED，而不影响同一端口上的其他引脚，还需要引入掩码机制，上述代码主要就是实现这些功能，实现完成之后通过编译，可以在串口交互界面输入下面的python代码，看是否可以控制LED</p><div class="code-container" data-rel="Py"><figure class="iseeu highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 假设板子上 LED 在 GPIO 0</span></span><br><span class="line">led = machine.Pin(<span class="number">0</span>, machine.Pin.OUT) <span class="comment">#先注册一个LED0，设置为输出</span></span><br><span class="line">led.value(<span class="number">1</span>)   <span class="comment"># 点亮</span></span><br><span class="line">led.value(<span class="number">0</span>)   <span class="comment"># 熄灭</span></span><br></pre></td></tr></table></figure></div><p>如果之前的步骤没有出问题，那么LED可以正常控制，那么这样简单的一个GPIO输入输出就OK啦</p><hr><h3 id="C-6-对于其它功能"><a href="#C-6-对于其它功能" class="headerlink" title="C.6 对于其它功能"></a>C.6 对于其它功能</h3><p>其它比如IIC SPI等功能，完全可以参考上面的GPIO功能进行封装实现，这里就不多说了……</p><hr><h2 id="D-遇到的问题"><a href="#D-遇到的问题" class="headerlink" title="D 遇到的问题"></a>D 遇到的问题</h2><p>这里可能会遇到的问题大多是工程模版创建和烧录的基本问题，大家可以去看模版创建的文章的问题小结</p><p>这里列举出已经解决的问题，解决方法都在之前的流程中</p><ol><li>Qstr无法生成 – ps 可以自己添加，如果添加的功能不是很多的情况下</li><li>无法创建变量</li><li>配置文件包含py/obj.h会报错</li><li>新模块无法注册</li></ol><hr><h2 id="F-小结"><a href="#F-小结" class="headerlink" title="F 小结"></a>F 小结</h2><p>如果后面添加更新了其它功能也会更新这个文章，如果有什么问题可以在下面/邮箱留言，我会及时回复</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li>网络各种资源、文档及AI</li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#51;&#x31;&#x34;&#x36;&#x37;&#x30;&#50;&#51;&#54;&#50;&#x40;&#x71;&#x71;&#46;&#99;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">主要记录对ZYNQ的裸机移植 micropython 的过程，外设仅涉及GPIO 串口</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    <category term="ZYNQ" scheme="https://blog.haozi-haozi.cn/categories/Embedded/ZYNQ/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="ZNYQ" scheme="https://blog.haozi-haozi.cn/tags/ZNYQ/"/>
    
    <category term="micropython" scheme="https://blog.haozi-haozi.cn/tags/micropython/"/>
    
  </entry>
  
  <entry>
    <title>真象还原 --文件系统/系统交互 study(5)</title>
    <link href="https://blog.haozi-haozi.cn/2025/11/10/os_elephant_five/"/>
    <id>https://blog.haozi-haozi.cn/2025/11/10/os_elephant_five/</id>
    <published>2025-11-10T07:01:00.000Z</published>
    <updated>2026-03-24T08:59:03.922Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A 前情提要"></a>A 前情提要</h2><p>在此之前，第一部分我们完成了基础的<strong>环境配置，bochs配置</strong>，以及<strong>MBR,loader</strong>的的基础编写，成功的进入了<strong>保护模式</strong>并且开启了<strong>内存分页功能</strong></p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/21/os_elephant_one/">真象还原 –环境/准备 study(1)</a></li></ul></blockquote><p>第二部分完成了对<strong>内联汇编</strong>，<strong>中断初始化</strong>，<strong>定时器初始化</strong>，也实现了<em>基础打印函数</em>*</p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/24/os_elephant_two/">真象还原 –内核/中断 study(2)</a></li></ul></blockquote><p>第三部分完成了对<strong>内存管理</strong>，<strong>线程</strong>，<strong>同步</strong>，也实现了<em>基础生产者与消费者</em>*</p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/28/os_elephant_three/">真象还原 –内存管理/线程/同步 study(3)</a></li></ul></blockquote><p>第四部分完成了对<strong>用户进程/完善内核(系统调用，完善内存)/硬盘驱动</strong>相关内容，也实现了<strong>printf</strong>等</p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/11/03/os_elephant_four/">真象还原 –用户进程/完善内核/硬盘驱动 study(4)</a></li></ul></blockquote><p>这一部分准备学习<strong>文件系统</strong>和<strong>系统交互</strong>这两个部分，也是这本书的最后部分！！！！！</p><p><strong>ps:如果参考本系列文章来实操，需要结合《操作系统真象还原》一起观看，否则会缺失很多细节</strong></p><h2 id="B-文件系统"><a href="#B-文件系统" class="headerlink" title="B 文件系统"></a>B 文件系统</h2><h3 id="B-1-创建文件系统"><a href="#B-1-创建文件系统" class="headerlink" title="B.1 创建文件系统"></a>B.1 创建文件系统</h3><p>我们先在创建一个文件夹<code>mouse/fs</code>,以后有关文件系统的内容就放在这里了，然后是先做一些准备工作,当然还要略微修改一下Makefile文件</p><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  内核构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/kernel/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># .PHONY 标明伪目标,它代表了一个需要被执行的动作或任务，而非一个需要被生成的文件</span></span><br><span class="line"><span class="comment"># 可以在本文件所在目录下 使用make all 、 make clean 等命令执行一系列任务</span></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: all kernel user clean img dirs</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 基础路径  =======================================</span></span><br><span class="line"><span class="comment"># 其中的 ":=" 表示立即展开使用，即右边的变量值会被立刻赋值，"$" 可以引用之前创建的变量</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line">MOUSE_DIR := ..</span><br><span class="line">BUILD_DIR := <span class="variable">$(MOUSE_DIR)</span>/build</span><br><span class="line">BUILD_KERNEL_DIR := <span class="variable">$(BUILD_DIR)</span>/kernel</span><br><span class="line">BUILD_USER_DIR := <span class="variable">$(BUILD_DIR)</span>/user</span><br><span class="line">IMG_PATH := /home/mouse/OS_mouse/tool/bochs/hd60M.img</span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 编译工具  =======================================</span></span><br><span class="line">CC := gcc</span><br><span class="line">ASM := nasm</span><br><span class="line">LD := ld</span><br><span class="line"></span><br><span class="line"><span class="comment"># C编译器标志，这里指出输出32位，编译器不识别/使用内建函数，禁用栈保护机制，指定当前为独立式环境(标准库不存在)，</span></span><br><span class="line"><span class="comment"># -I 分别包含需要的头文件路径(共用库，内核库，用户库，内核，设备,线程，用户进程)</span></span><br><span class="line">CFLAGS := -m32 -fno-builtin -fno-stack-protector -ffreestanding \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/lib -I<span class="variable">$(MOUSE_DIR)</span>/lib/kernel -I<span class="variable">$(MOUSE_DIR)</span>/lib/user \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/kernel -I<span class="variable">$(MOUSE_DIR)</span>/device -I<span class="variable">$(MOUSE_DIR)</span>/thread -I<span class="variable">$(MOUSE_DIR)</span>/fs -I<span class="variable">$(MOUSE_DIR)</span>/userprog -c</span><br><span class="line">ASMFLAGS := -f elf</span><br><span class="line">LDFLAGS := -m elf_i386 -Ttext 0xc0001500 -e main</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===================== 包含子Makefile，导入子模块源文件  ===============================</span></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/device/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/thread/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/userprog/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/fs/Makefile</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============================== 本目录源文件  ===========================================</span></span><br><span class="line">KERNEL_SRCS := main.c init.c interrupt.c debug.c memory.c</span><br><span class="line">KERNEL_ASMS := kernel.S</span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 为各模块添加前缀路径  =======================================</span></span><br><span class="line"><span class="comment"># $(addprefix &lt;prefix&gt;,&lt;names&gt;)是一个内置函数，能将路径prefix添加到names前</span></span><br><span class="line"><span class="comment"># ======================================================================================</span></span><br><span class="line">KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_SRCS)</span>)</span></span><br><span class="line">KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_SRCS)</span>)</span></span><br><span class="line">LIB_KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_USER_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_SRCS)</span>)</span></span><br><span class="line">LIB_USER_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">DEVICE_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_SRCS)</span>)</span></span><br><span class="line">DEVICE_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_SRCS)</span>)</span></span><br><span class="line">LIB_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">THREAD_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/thread/,<span class="variable">$(THREAD_SRCS)</span>)</span></span><br><span class="line">THREAD_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/thread/,<span class="variable">$(THREAD_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">USERPROG_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/userprog/,<span class="variable">$(USERPROG_SRCS)</span>)</span></span><br><span class="line">USERPROG_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/userprog/,<span class="variable">$(USERPROG_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">FS_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/fs/,<span class="variable">$(FS_SRCS)</span>)</span></span><br><span class="line">FS_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/fs/,<span class="variable">$(FS_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 汇总全部源文件  ================================================</span></span><br><span class="line">ALL_C_SRCS := <span class="variable">$(KERNEL_SRCS)</span> <span class="variable">$(LIB_KERNEL_SRCS)</span> <span class="variable">$(LIB_USER_SRCS)</span> <span class="variable">$(DEVICE_SRCS)</span> <span class="variable">$(LIB_SRCS)</span> <span class="variable">$(THREAD_SRCS)</span> <span class="variable">$(USERPROG_SRCS)</span> <span class="variable">$(FS_SRCS)</span></span><br><span class="line">ALL_ASMS   := <span class="variable">$(KERNEL_ASMS)</span> <span class="variable">$(LIB_KERNEL_ASMS)</span> <span class="variable">$(LIB_USER_ASMS)</span> <span class="variable">$(DEVICE_ASMS)</span> <span class="variable">$(LIB_ASMS)</span> <span class="variable">$(THREAD_ASMS)</span> <span class="variable">$(USERPROG_ASMS)</span> <span class="variable">$(FS_ASMS)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 生成对应的 .o 文件路径  ========================================</span></span><br><span class="line"><span class="comment"># $(filter &lt;pattern...&gt;,&lt;text&gt;)：这个函数用于从 &lt;text&gt;中筛选出符合模式 &lt;pattern&gt;的单词</span></span><br><span class="line"><span class="comment"># $(patsubst &lt;pattern&gt;,&lt;replacement&gt;,&lt;text&gt;)：将 &lt;text&gt;中所有匹配 &lt;pattern&gt;的单词替换为 </span></span><br><span class="line"><span class="comment"># &lt;replacement&gt;的形式，&lt;pattern&gt;中可以使用通配符 %</span></span><br><span class="line"><span class="comment"># 通过这个函数，将生成的.o文件分别生成到build路径下的不同路径(这里除了用户，都生成到/build/kernel)</span></span><br><span class="line"><span class="comment"># =========================================================================================</span></span><br><span class="line">OBJS_KERNEL := \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,<span class="variable">$(ALL_C_SRCS)</span>)</span>) \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,<span class="variable">$(ALL_ASMS)</span>)</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># OBJS_KERNEL := \</span></span><br><span class="line"><span class="comment">#   $(patsubst $(MOUSE_DIR)/%.c,$(BUILD_KERNEL_DIR)/%.o,$(filter $(MOUSE_DIR)/%.c,$(filter-out $(MOUSE_DIR)/lib/user/%,$(ALL_C_SRCS)))) \</span></span><br><span class="line"><span class="comment">#   $(patsubst $(MOUSE_DIR)/%.S,$(BUILD_KERNEL_DIR)/%.o,$(filter $(MOUSE_DIR)/%.S,$(filter-out $(MOUSE_DIR)/lib/user/%,$(ALL_ASMS))))</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># OBJS_USER := \</span></span><br><span class="line"><span class="comment">#   $(patsubst $(MOUSE_DIR)/lib/user/%.c,$(BUILD_USER_DIR)/%.o,$(filter $(MOUSE_DIR)/lib/user/%.c,$(ALL_C_SRCS))) \</span></span><br><span class="line"><span class="comment">#   $(patsubst $(MOUSE_DIR)/lib/user/%.S,$(BUILD_USER_DIR)/%.o,$(filter $(MOUSE_DIR)/lib/user/%.S,$(ALL_ASMS)))</span></span><br><span class="line"></span><br><span class="line">KERNEL_BIN := <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line"></span><br><span class="line"><span class="comment"># ==================================== 主要规则  ==========================================</span></span><br><span class="line"><span class="section">all: dirs kernel user</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 以@开头的指令，终端只会显示命令的输出，不显示命令本身--这里创建build文件夹</span></span><br><span class="line"><span class="section">dirs:</span></span><br><span class="line">@echo <span class="string">"Creating build directories..."</span></span><br><span class="line">@mkdir -p <span class="variable">$(BUILD_KERNEL_DIR)</span> <span class="variable">$(BUILD_USER_DIR)</span></span><br><span class="line"></span><br><span class="line"><span class="section">kernel: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line"><span class="section">user: <span class="variable">$(OBJS_USER)</span></span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(KERNEL_BIN)</span>: <span class="variable">$(OBJS_KERNEL)</span> <span class="comment">#$(OBJS_USER)</span></span><br><span class="line">@echo <span class="string">"Linking kernel object files to generate kernel.bin..."</span></span><br><span class="line"><span class="variable">$(LD)</span> <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,<span class="variable">$^</span>)</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_USER_DIR)</span>/%.o,<span class="variable">$^</span>)</span></span><br><span class="line">@echo <span class="string">"Kernel linking completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================= 编译规则 ============================================</span></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling user C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling user S: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/userprog/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling userprog C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/userprog/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling userprog S: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 镜像生成 ==========================================</span></span><br><span class="line"><span class="section">img: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line">@echo <span class="string">"Writing kernel image to disk..."</span></span><br><span class="line">dd if=<span class="variable">$(KERNEL_BIN)</span> of=<span class="variable">$(IMG_PATH)</span> bs=512 count=200 seek=9 conv=notrunc</span><br><span class="line">@echo <span class="string">"Kernel image writing completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 清理规则 ==========================================</span></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">@echo <span class="string">"Cleaning build files..."</span></span><br><span class="line">-rm -f <span class="variable">$(BUILD_KERNEL_DIR)</span>/*.o <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line">-rm -f <span class="variable">$(BUILD_USER_DIR)</span>/*.o</span><br><span class="line">-rm -rf <span class="variable">$(BUILD_DIR)</span></span><br><span class="line">@echo <span class="string">"Cleanup completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="section">mouse: clean all img</span></span><br><span class="line">@echo <span class="string">"Mouse build process (clean -&gt; all -&gt; img) completed!"</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  子文件 /home/mouse/OS_mouse/tool/bochs/mouse/fs/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line">FS_SRCS := fs.c      <span class="comment"># 例如：thread.c, scheduler.c</span></span><br><span class="line">FS_ASMS :=              <span class="comment"># 例如：thread_switch.S</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果没有汇编文件，THREAD_ASMS 应为空</span></span><br></pre></td></tr></table></figure></div><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_071.webp" width="400"><p>这个图片就是我们的简易文件系统的布局(来自书中)</p><hr><h4 id="B1-1-创建超级块、i-结点、目录项"><a href="#B1-1-创建超级块、i-结点、目录项" class="headerlink" title="B1.1 创建超级块、i 结点、目录项"></a>B1.1 创建超级块、i 结点、目录项</h4><p>话不多说，概念还是不说了，直接搓代码,这里创建了四个头文件，都使用用来描述文件系统一些结构体定义等…..</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/fs/super_blcok.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __FS_SUPER_BLOCK_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __FS_SUPER_BLOCK_H、</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//超级块</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>{</span></span><br><span class="line">    <span class="type">uint32_t</span> magic;             <span class="comment">// 用来标识文件系统类型，适用于多文件系统用来判断使用</span></span><br><span class="line">    <span class="type">uint32_t</span> sec_cnt;           <span class="comment">// 本分区总共的扇区数</span></span><br><span class="line">    <span class="type">uint32_t</span> inode_cnt;         <span class="comment">// 本分区中 inode 数量</span></span><br><span class="line">    <span class="type">uint32_t</span> part_lba_base;     <span class="comment">// 本分区的起始 lba 地址</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> block_bitmap_lba;      <span class="comment">// 块位图本身起始扇区地址</span></span><br><span class="line">    <span class="type">uint32_t</span> block_bitmap_sects;    <span class="comment">// 扇区位图本身占用的扇区数量</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> inode_bitmap_lba;      <span class="comment">// i 结点位图起始扇区 lba 地址</span></span><br><span class="line">    <span class="type">uint32_t</span> inode_bitmap_sects;    <span class="comment">// i 结点位图占用的扇区数量</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> inode_table_lba;       <span class="comment">// i 结点表起始扇区 lba 地址</span></span><br><span class="line">    <span class="type">uint32_t</span> inode_table_sects;     <span class="comment">// i 结点表占用的扇区数量</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> data_start_lba;        <span class="comment">// 数据区开始的第一个扇区号</span></span><br><span class="line">    <span class="type">uint32_t</span> root_inode_no;         <span class="comment">// 根目录所在的 I 结点号</span></span><br><span class="line">    <span class="type">uint32_t</span> dir_entry_size;        <span class="comment">// 目录项大小</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint8_t</span> pad[<span class="number">460</span>];               <span class="comment">// 加上 460 字节，凑够 512 字节 1 扇区大小</span></span><br><span class="line">} __attribute__ ((packed));</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/fs/inode.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __FS_INODE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __FS_INODE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// inode 结构 </span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">inode</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span> i_no; <span class="comment">// inode 编号</span></span><br><span class="line">    <span class="comment">/*  当此 inode 是文件时，i_size 是指文件大小, </span></span><br><span class="line"><span class="comment">        若此 inode 是目录，i_size 是指该目录下所有目录项大小之和*/</span> </span><br><span class="line">    <span class="type">uint32_t</span> i_size; </span><br><span class="line">    <span class="type">uint32_t</span> i_open_cnts;   <span class="comment">// 记录此文件被打开的次数</span></span><br><span class="line">    <span class="type">bool</span> write_deny;        <span class="comment">// 写文件不能并行，进程写文件前检查此标识</span></span><br><span class="line">    <span class="comment">/* i_sectors[0-11]是直接块，i_sectors[12]用来存储一级间接块指针 */</span> </span><br><span class="line">    <span class="type">uint32_t</span> i_sectors[<span class="number">13</span>]; </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">inode_tag</span>;</span>     <span class="comment">//记录是否是已经打开的inode</span></span><br><span class="line">}; </span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/fs/dir.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __FS_DIR_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __FS_DIR_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"fs.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX_FILE_NAME_LEN 16 <span class="comment">// 最大文件名长度</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 目录结构</span></span><br><span class="line"><span class="comment">// 并不在磁盘上存在，只用于与目录相关的操作时，在内存中创建的结构，用过之后就释放了，不会回写到磁盘中</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">dir</span> {</span> </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">inode</span>;</span> </span><br><span class="line">    <span class="type">uint32_t</span> dir_pos;       <span class="comment">// 记录在目录内的偏移</span></span><br><span class="line">    <span class="type">uint8_t</span> dir_buf[<span class="number">512</span>];   <span class="comment">// 目录的数据缓存</span></span><br><span class="line">}; </span><br><span class="line"></span><br><span class="line"><span class="comment">// 目录项结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span> {</span> </span><br><span class="line">    <span class="type">char</span> filename[MAX_FILE_NAME_LEN];   <span class="comment">// 普通文件或目录名称</span></span><br><span class="line">    <span class="type">uint32_t</span> i_no;                      <span class="comment">// 普通文件或目录对应的 inode 编号</span></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">file_types</span> <span class="title">f_type</span>;</span>             <span class="comment">// 文件类型</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/fs/fs.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __FS_FS_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __FS_FS_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX_FILES_PER_PART 4096 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 每个分区所支持最大创建的文件数</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BITS_PER_SECTOR 4096 <span class="comment">// 每扇区的位数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SECTOR_SIZE 512 <span class="comment">// 扇区字节大小</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BLOCK_SIZE SECTOR_SIZE <span class="comment">// 块字节大小</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 文件类型</span></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">file_types</span> {</span> </span><br><span class="line">    FT_UNKNOWN,     <span class="comment">// 不支持的文件类型</span></span><br><span class="line">    FT_REGULAR,     <span class="comment">// 普通文件</span></span><br><span class="line">    FT_DIRECTORY    <span class="comment">// 目录</span></span><br><span class="line">};</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><hr><h4 id="B1-2-创建文件系统"><a href="#B1-2-创建文件系统" class="headerlink" title="B1.2 创建文件系统"></a>B1.2 创建文件系统</h4><p>本节开始创建文件系统，也就是平时咱们所说的高级格式化分区，相关的代码在<code>fs/fs.c</code>中,下面直接给出完整代码:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/fs/fs.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"fs.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"super_block.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"inode.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"dir.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio-kernel.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ide.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 格式化分区,也就是初始化分区的元信息,创建文件系统 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">partition_format</span><span class="params">(<span class="keyword">struct</span> partition* part)</span> {</span><br><span class="line"><span class="comment">/* 为方便实现,一个块大小是一扇区 */</span></span><br><span class="line">   <span class="type">uint32_t</span> boot_sector_sects = <span class="number">1</span>;  </span><br><span class="line">   <span class="type">uint32_t</span> super_block_sects = <span class="number">1</span>;</span><br><span class="line">   <span class="type">uint32_t</span> inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR);   <span class="comment">// I结点位图占用的扇区数.最多支持4096个文件</span></span><br><span class="line">   <span class="type">uint32_t</span> inode_table_sects = DIV_ROUND_UP(((<span class="keyword">sizeof</span>(<span class="keyword">struct</span> inode) * MAX_FILES_PER_PART)), SECTOR_SIZE);</span><br><span class="line">   <span class="type">uint32_t</span> used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects;</span><br><span class="line">   <span class="type">uint32_t</span> free_sects = part-&gt;sec_cnt - used_sects;  </span><br><span class="line"></span><br><span class="line"><span class="comment">/************** 简单处理块位图占据的扇区数 ***************/</span></span><br><span class="line">   <span class="type">uint32_t</span> block_bitmap_sects;</span><br><span class="line">   block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);</span><br><span class="line">   <span class="comment">/* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */</span></span><br><span class="line">   <span class="type">uint32_t</span> block_bitmap_bit_len = free_sects - block_bitmap_sects; </span><br><span class="line">   block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR); </span><br><span class="line"><span class="comment">/*********************************************************/</span></span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 超级块初始化 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span> <span class="title">sb</span>;</span></span><br><span class="line">   sb.magic = <span class="number">0x19590318</span>;</span><br><span class="line">   sb.sec_cnt = part-&gt;sec_cnt;</span><br><span class="line">   sb.inode_cnt = MAX_FILES_PER_PART;</span><br><span class="line">   sb.part_lba_base = part-&gt;start_lba;</span><br><span class="line"></span><br><span class="line">   sb.block_bitmap_lba = sb.part_lba_base + <span class="number">2</span>; <span class="comment">// 第0块是引导块,第1块是超级块</span></span><br><span class="line">   sb.block_bitmap_sects = block_bitmap_sects;</span><br><span class="line"></span><br><span class="line">   sb.inode_bitmap_lba = sb.block_bitmap_lba + sb.block_bitmap_sects;</span><br><span class="line">   sb.inode_bitmap_sects = inode_bitmap_sects;</span><br><span class="line"></span><br><span class="line">   sb.inode_table_lba = sb.inode_bitmap_lba + sb.inode_bitmap_sects;</span><br><span class="line">   sb.inode_table_sects = inode_table_sects; </span><br><span class="line"></span><br><span class="line">   sb.data_start_lba = sb.inode_table_lba + sb.inode_table_sects;</span><br><span class="line">   sb.root_inode_no = <span class="number">0</span>;</span><br><span class="line">   sb.dir_entry_size = <span class="keyword">sizeof</span>(<span class="keyword">struct</span> dir_entry);</span><br><span class="line"></span><br><span class="line">   printk(<span class="string">"%s info:\n"</span>, part-&gt;name);</span><br><span class="line">   printk(<span class="string">"   magic:0x%x\n   part_lba_base:0x%x\n   all_sectors:0x%x\n   inode_cnt:0x%x\n   block_bitmap_lba:0x%x\n   block_bitmap_sectors:0x%x\n   inode_bitmap_lba:0x%x\n   inode_bitmap_sectors:0x%x\n   inode_table_lba:0x%x\n   inode_table_sectors:0x%x\n   data_start_lba:0x%x\n"</span>, sb.magic, sb.part_lba_base, sb.sec_cnt, sb.inode_cnt, sb.block_bitmap_lba, sb.block_bitmap_sects, sb.inode_bitmap_lba, sb.inode_bitmap_sects, sb.inode_table_lba, sb.inode_table_sects, sb.data_start_lba);</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">hd</span> =</span> part-&gt;my_disk;</span><br><span class="line"><span class="comment">/*******************************</span></span><br><span class="line"><span class="comment"> * 1 将超级块写入本分区的1扇区 *</span></span><br><span class="line"><span class="comment"> ******************************/</span></span><br><span class="line">   ide_write(hd, part-&gt;start_lba + <span class="number">1</span>, &amp;sb, <span class="number">1</span>);</span><br><span class="line">   printk(<span class="string">"   super_block_lba:0x%x\n"</span>, part-&gt;start_lba + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 找出数据量最大的元信息,用其尺寸做存储缓冲区*/</span></span><br><span class="line">   <span class="type">uint32_t</span> buf_size = (sb.block_bitmap_sects &gt;= sb.inode_bitmap_sects ? sb.block_bitmap_sects : sb.inode_bitmap_sects);</span><br><span class="line">   buf_size = (buf_size &gt;= sb.inode_table_sects ? buf_size : sb.inode_table_sects) * SECTOR_SIZE;</span><br><span class="line">   <span class="type">uint8_t</span>* buf = (<span class="type">uint8_t</span>*)sys_malloc(buf_size);<span class="comment">// 申请的内存由内存管理系统清0后返回</span></span><br><span class="line">   </span><br><span class="line"><span class="comment">/**************************************</span></span><br><span class="line"><span class="comment"> * 2 将块位图初始化并写入sb.block_bitmap_lba *</span></span><br><span class="line"><span class="comment"> *************************************/</span></span><br><span class="line">   <span class="comment">/* 初始化块位图block_bitmap */</span></span><br><span class="line">   buf[<span class="number">0</span>] |= <span class="number">0x01</span>;       <span class="comment">// 第0个块预留给根目录,位图中先占位</span></span><br><span class="line">   <span class="type">uint32_t</span> block_bitmap_last_byte = block_bitmap_bit_len / <span class="number">8</span>;</span><br><span class="line">   <span class="type">uint8_t</span>  block_bitmap_last_bit  = block_bitmap_bit_len % <span class="number">8</span>;</span><br><span class="line">   <span class="type">uint32_t</span> last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE);     <span class="comment">// last_size是位图所在最后一个扇区中，不足一扇区的其余部分</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 1 先将位图最后一字节到其所在的扇区的结束全置为1,即超出实际块数的部分直接置为已占用*/</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;buf[block_bitmap_last_byte], <span class="number">0xff</span>, last_size);</span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 2 再将上一步中覆盖的最后一字节内的有效位重新置0 */</span></span><br><span class="line">   <span class="type">uint8_t</span> bit_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (bit_idx &lt;= block_bitmap_last_bit) {</span><br><span class="line">      buf[block_bitmap_last_byte] &amp;= ~(<span class="number">1</span> &lt;&lt; bit_idx++);</span><br><span class="line">   }</span><br><span class="line">   ide_write(hd, sb.block_bitmap_lba, buf, sb.block_bitmap_sects);</span><br><span class="line"></span><br><span class="line"><span class="comment">/***************************************</span></span><br><span class="line"><span class="comment"> * 3 将inode位图初始化并写入sb.inode_bitmap_lba *</span></span><br><span class="line"><span class="comment"> ***************************************/</span></span><br><span class="line">   <span class="comment">/* 先清空缓冲区*/</span></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, buf_size);</span><br><span class="line">   buf[<span class="number">0</span>] |= <span class="number">0x1</span>;      <span class="comment">// 第0个inode分给了根目录</span></span><br><span class="line">   <span class="comment">/* 由于inode_table中共4096个inode,位图inode_bitmap正好占用1扇区,</span></span><br><span class="line"><span class="comment">    * 即inode_bitmap_sects等于1, 所以位图中的位全都代表inode_table中的inode,</span></span><br><span class="line"><span class="comment">    * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,</span></span><br><span class="line"><span class="comment">    * inode_bitmap所在的扇区中没有多余的无效位 */</span></span><br><span class="line">   ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);</span><br><span class="line"></span><br><span class="line"><span class="comment">/***************************************</span></span><br><span class="line"><span class="comment"> * 4 将inode数组初始化并写入sb.inode_table_lba *</span></span><br><span class="line"><span class="comment"> ***************************************/</span></span><br><span class="line"> <span class="comment">/* 准备写inode_table中的第0项,即根目录所在的inode */</span></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, buf_size);  <span class="comment">// 先清空缓冲区buf</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">i</span> =</span> (<span class="keyword">struct</span> inode*)buf; </span><br><span class="line">   i-&gt;i_size = sb.dir_entry_size * <span class="number">2</span>; <span class="comment">// .和..</span></span><br><span class="line">   i-&gt;i_no = <span class="number">0</span>;   <span class="comment">// 根目录占inode数组中第0个inode</span></span><br><span class="line">   i-&gt;i_sectors[<span class="number">0</span>] = sb.data_start_lba;     <span class="comment">// 由于上面的memset,i_sectors数组的其它元素都初始化为0 </span></span><br><span class="line">   ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);</span><br><span class="line">   </span><br><span class="line"><span class="comment">/***************************************</span></span><br><span class="line"><span class="comment"> * 5 将根目录初始化并写入sb.data_start_lba</span></span><br><span class="line"><span class="comment"> ***************************************/</span></span><br><span class="line">   <span class="comment">/* 写入根目录的两个目录项.和.. */</span></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, buf_size);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">p_de</span> =</span> (<span class="keyword">struct</span> dir_entry*)buf;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化当前目录"." */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(p_de-&gt;filename, <span class="string">"."</span>, <span class="number">1</span>);</span><br><span class="line">   p_de-&gt;i_no = <span class="number">0</span>;</span><br><span class="line">   p_de-&gt;f_type = FT_DIRECTORY;</span><br><span class="line">   p_de++;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化当前目录父目录".." */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(p_de-&gt;filename, <span class="string">".."</span>, <span class="number">2</span>);</span><br><span class="line">   p_de-&gt;i_no = <span class="number">0</span>;   <span class="comment">// 根目录的父目录依然是根目录自己</span></span><br><span class="line">   p_de-&gt;f_type = FT_DIRECTORY;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */</span></span><br><span class="line">   ide_write(hd, sb.data_start_lba, buf, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">   printk(<span class="string">"   root_dir_lba:0x%x\n"</span>, sb.data_start_lba);</span><br><span class="line">   printk(<span class="string">"%s format done\n"</span>, part-&gt;name);</span><br><span class="line">   sys_free(buf);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">filesys_init</span><span class="params">()</span> {</span><br><span class="line">   <span class="type">uint8_t</span> channel_no = <span class="number">0</span>, dev_no, part_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* sb_buf用来存储从硬盘上读入的超级块 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>* <span class="title">sb_buf</span> =</span> (<span class="keyword">struct</span> super_block*)sys_malloc(SECTOR_SIZE);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (sb_buf == <span class="literal">NULL</span>) {</span><br><span class="line">      PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">   }</span><br><span class="line">   printk(<span class="string">"searching filesystem......\n"</span>);</span><br><span class="line">   <span class="keyword">while</span> (channel_no &lt; channel_cnt) {</span><br><span class="line">      dev_no = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span>(dev_no &lt; <span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">if</span> (dev_no == <span class="number">0</span>) {   <span class="comment">// 跨过裸盘hd60M.img</span></span><br><span class="line">    dev_no++;</span><br><span class="line">    <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">hd</span> =</span> &amp;channels[channel_no].devices[dev_no];</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">part</span> =</span> hd-&gt;prim_parts;</span><br><span class="line"> <span class="keyword">while</span>(part_idx &lt; <span class="number">12</span>) {   <span class="comment">// 4个主分区+8个逻辑</span></span><br><span class="line">    <span class="keyword">if</span> (part_idx == <span class="number">4</span>) {  <span class="comment">// 开始处理逻辑分区</span></span><br><span class="line">       part = hd-&gt;logic_parts;</span><br><span class="line">    }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* channels数组是全局变量,默认值为0,disk属于其嵌套结构,</span></span><br><span class="line"><span class="comment">  * partition又为disk的嵌套结构,因此partition中的成员默认也为0.</span></span><br><span class="line"><span class="comment">  * 若partition未初始化,则partition中的成员仍为0. </span></span><br><span class="line"><span class="comment">  * 下面处理存在的分区. */</span></span><br><span class="line">    <span class="keyword">if</span> (part-&gt;sec_cnt != <span class="number">0</span>) {  <span class="comment">// 如果分区存在</span></span><br><span class="line">       <span class="built_in">memset</span>(sb_buf, <span class="number">0</span>, SECTOR_SIZE);</span><br><span class="line"></span><br><span class="line">       <span class="comment">/* 读出分区的超级块,根据魔数是否正确来判断是否存在文件系统 */</span></span><br><span class="line">       ide_read(hd, part-&gt;start_lba + <span class="number">1</span>, sb_buf, <span class="number">1</span>);   </span><br><span class="line"></span><br><span class="line">       <span class="comment">/* 只支持自己的文件系统.若磁盘上已经有文件系统就不再格式化了 */</span></span><br><span class="line">       <span class="keyword">if</span> (sb_buf-&gt;magic == <span class="number">0x19590318</span>) {</span><br><span class="line">  printk(<span class="string">"%s has filesystem\n"</span>, part-&gt;name);</span><br><span class="line">       } <span class="keyword">else</span> {  <span class="comment">// 其它文件系统不支持,一律按无文件系统处理</span></span><br><span class="line">  printk(<span class="string">"formatting %s`s partition %s......\n"</span>, hd-&gt;name, part-&gt;name);</span><br><span class="line">  partition_format(part);</span><br><span class="line">       }</span><br><span class="line">    }</span><br><span class="line">    part_idx++;</span><br><span class="line">    part++;<span class="comment">// 下一分区</span></span><br><span class="line"> }</span><br><span class="line"> dev_no++;<span class="comment">// 下一磁盘</span></span><br><span class="line">      }</span><br><span class="line">      channel_no++;<span class="comment">// 下一通道</span></span><br><span class="line">   }</span><br><span class="line">   sys_free(sb_buf);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>注意要在初始化总文件里面添加<code>filesys_init()</code>函数</p><p>这里可以运行测试，第一次运行会显示自动创建很多了新的文件系统(格式化)，因为原本磁盘中是没有的；第二次运行会发现显示的是已经存在了文件系统，不会再进行格式化</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#第一次</span></span><br><span class="line">sdb8 format <span class="keyword">done</span></span><br><span class="line">formatting sdb<span class="string">'s partition sdb9..</span></span><br><span class="line"><span class="string">sdb9 info:</span></span><br><span class="line"><span class="string">magic:0x19590318</span></span><br><span class="line"><span class="string">part_lba_base:Ox1D8BF</span></span><br><span class="line"><span class="string">all_sectors:OxA521</span></span><br><span class="line"><span class="string">inode_cnt:0x1000</span></span><br><span class="line"><span class="string">block_bitmap_lba:Ox1D8C1</span></span><br><span class="line"><span class="string">block_bitmap_sectors:OxB</span></span><br><span class="line"><span class="string">inode_bitmap_lba:Ox1D8CC</span></span><br><span class="line"><span class="string">inode_bitmap_sectors:0x1</span></span><br><span class="line"><span class="string">inode_table_Iba:Ox1D8CD</span></span><br><span class="line"><span class="string">inode_table_sectors:0x260</span></span><br><span class="line"><span class="string">data_start_lba:Ox1DB2D</span></span><br><span class="line"><span class="string">super_block_lba:Ox1D8C0</span></span><br><span class="line"><span class="string">root_dir_lba:Ox1DB2D</span></span><br><span class="line"><span class="string">sdb9 format done</span></span><br><span class="line"><span class="string">init_all done</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#第二次</span></span><br><span class="line">ide_init <span class="keyword">done</span></span><br><span class="line">searching filesystem.....</span><br><span class="line">sdb1 has filesystem</span><br><span class="line">sdb5 has filesystem</span><br><span class="line">sdb6 has filesystem</span><br><span class="line">sdb7 has filesystem</span><br><span class="line">sdb8 has filesystem</span><br><span class="line">sdb9 has filesystem</span><br><span class="line">init_all <span class="keyword">done</span></span><br></pre></td></tr></table></figure></div><hr><h4 id="B1-3-挂载分区"><a href="#B1-3-挂载分区" class="headerlink" title="B1.3 挂载分区"></a>B1.3 挂载分区</h4><blockquote><p>Windows 系统的分区盘符简单明了，C、D、E 盘直接摆在那，用不用都看得到。而 Linux 中却大不相同，分区使用的时候可以单独“拿”出来，不用的时候还可以“收”起来</p></blockquote><p>这里来实现挂载<code>sdb1</code>：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/fs/fs.h</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.........</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">cur_part</span>;</span> <span class="comment">// 默认情况下操作的是哪个分区</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在分区链表中找到名为part_name的分区,并将其指针赋值给cur_part */</span></span><br><span class="line"><span class="type">static</span> <span class="type">bool</span> <span class="title function_">mount_partition</span><span class="params">(<span class="keyword">struct</span> list_elem* pelem, <span class="type">int</span> arg)</span> {</span><br><span class="line">   <span class="type">char</span>* part_name = (<span class="type">char</span>*)arg;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">part</span> =</span> elem2entry(<span class="keyword">struct</span> partition, part_tag, pelem);</span><br><span class="line">   <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(part-&gt;name, part_name)) {</span><br><span class="line">      cur_part = part;</span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">hd</span> =</span> cur_part-&gt;my_disk;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* sb_buf用来存储从硬盘上读入的超级块 */</span></span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>* <span class="title">sb_buf</span> =</span> (<span class="keyword">struct</span> super_block*)sys_malloc(SECTOR_SIZE);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 在内存中创建分区cur_part的超级块 */</span></span><br><span class="line">      cur_part-&gt;sb = (<span class="keyword">struct</span> super_block*)sys_malloc(<span class="keyword">sizeof</span>(<span class="keyword">struct</span> super_block));</span><br><span class="line">      <span class="keyword">if</span> (cur_part-&gt;sb == <span class="literal">NULL</span>) {</span><br><span class="line"> PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 读入超级块 */</span></span><br><span class="line">      <span class="built_in">memset</span>(sb_buf, <span class="number">0</span>, SECTOR_SIZE);</span><br><span class="line">      ide_read(hd, cur_part-&gt;start_lba + <span class="number">1</span>, sb_buf, <span class="number">1</span>);   </span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 把sb_buf中超级块的信息复制到分区的超级块sb中。*/</span></span><br><span class="line">      <span class="built_in">memcpy</span>(cur_part-&gt;sb, sb_buf, <span class="keyword">sizeof</span>(<span class="keyword">struct</span> super_block)); </span><br><span class="line"></span><br><span class="line">      <span class="comment">/**********     将硬盘上的块位图读入到内存    ****************/</span></span><br><span class="line">      cur_part-&gt;block_bitmap.bits = (<span class="type">uint8_t</span>*)sys_malloc(sb_buf-&gt;block_bitmap_sects * SECTOR_SIZE);</span><br><span class="line">      <span class="keyword">if</span> (cur_part-&gt;block_bitmap.bits == <span class="literal">NULL</span>) {</span><br><span class="line"> PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">      }</span><br><span class="line">      cur_part-&gt;block_bitmap.btmp_bytes_len = sb_buf-&gt;block_bitmap_sects * SECTOR_SIZE;</span><br><span class="line">      <span class="comment">/* 从硬盘上读入块位图到分区的block_bitmap.bits */</span></span><br><span class="line">      ide_read(hd, sb_buf-&gt;block_bitmap_lba, cur_part-&gt;block_bitmap.bits, sb_buf-&gt;block_bitmap_sects);   </span><br><span class="line">      <span class="comment">/*************************************************************/</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/**********     将硬盘上的inode位图读入到内存    ************/</span></span><br><span class="line">      cur_part-&gt;inode_bitmap.bits = (<span class="type">uint8_t</span>*)sys_malloc(sb_buf-&gt;inode_bitmap_sects * SECTOR_SIZE);</span><br><span class="line">      <span class="keyword">if</span> (cur_part-&gt;inode_bitmap.bits == <span class="literal">NULL</span>) {</span><br><span class="line"> PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">      }</span><br><span class="line">      cur_part-&gt;inode_bitmap.btmp_bytes_len = sb_buf-&gt;inode_bitmap_sects * SECTOR_SIZE;</span><br><span class="line">      <span class="comment">/* 从硬盘上读入inode位图到分区的inode_bitmap.bits */</span></span><br><span class="line">      ide_read(hd, sb_buf-&gt;inode_bitmap_lba, cur_part-&gt;inode_bitmap.bits, sb_buf-&gt;inode_bitmap_sects);   </span><br><span class="line">      <span class="comment">/*************************************************************/</span></span><br><span class="line"></span><br><span class="line">      list_init(&amp;cur_part-&gt;open_inodes);</span><br><span class="line">      printk(<span class="string">"mount %s done!\n"</span>, part-&gt;name);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。</span></span><br><span class="line"><span class="comment">      只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/</span></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;     <span class="comment">// 使list_traversal继续遍历</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.........</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">filesys_init</span><span class="params">()</span> {</span><br><span class="line"></span><br><span class="line">    <span class="comment">//略.........</span></span><br><span class="line"></span><br><span class="line">   sys_free(sb_buf);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 确定默认操作的分区 */</span></span><br><span class="line">   <span class="type">char</span> default_part[<span class="number">8</span>] = <span class="string">"sdb1"</span>;</span><br><span class="line">   <span class="comment">/* 挂载分区 */</span></span><br><span class="line">   list_traversal(&amp;partition_list, mount_partition, (<span class="type">int</span>)default_part);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后重新运行可以在结尾看到一句话<code>mount sdb1 done!</code></p><hr><h3 id="B-2-文件描述符"><a href="#B-2-文件描述符" class="headerlink" title="B.2 文件描述符"></a>B.2 文件描述符</h3><p>这里跳过了文件描述符的简介……</p><blockquote><p>笼统地说，创建文件描述符的过程就是逐层在这三个数据结构中找空位，在该空位填充好数据后返回该位置的地址，比如：<br>（1）在全局的 inode 队列中新建一 inode（这肯定是在空位置处新建），然后返回该 inode 地址。<br>（2）在全局的文件表中的找一空位，在该位置填充文件结构，使其 fd_inode 指向上一步中返回的 inode<br>地址，然后返回本文件结构在文件表中的下标值。<br>（3）在 PCB 中的文件描述符数组中找一空位，使该位置的值指向上一步中返回的文件结构下标，并返回本文件描述符在文件描述符数组中的下标值</p></blockquote><h4 id="B2-1-文件描述符的实现"><a href="#B2-1-文件描述符的实现" class="headerlink" title="B2.1 文件描述符的实现"></a>B2.1 文件描述符的实现</h4><p>首先，修改pcb的结构体,创建文件描述符数组，并在<code>init_thread</code>中初始化它</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.h</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.....</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX_FILES_OPEN_PER_PROC 8   <span class="comment">//每个任务可以打开的文件数</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.....</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进程或线程的 pcb，程序控制块 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span>* self_kstack;      <span class="comment">// 各内核线程都用自己的内核栈,注意为首位</span></span><br><span class="line">    <span class="type">pid_t</span> pid;                  <span class="comment">// 任务的pid</span></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">task_status</span> <span class="title">status</span>;</span>    <span class="comment">// 任务状态</span></span><br><span class="line">    <span class="type">uint8_t</span> priority;           <span class="comment">// 线程优先级</span></span><br><span class="line">    <span class="type">char</span> name[<span class="number">16</span>];              <span class="comment">// 名称</span></span><br><span class="line">    <span class="type">uint8_t</span> ticks;              <span class="comment">// 每次在处理器上执行的时间滴答数,每次时钟中断都会对其减1，到0则换下处理器</span></span><br><span class="line">    <span class="type">uint32_t</span> elapsed_ticks;     <span class="comment">// 从cpu运行至今占用了多少cpu滴答数</span></span><br><span class="line">    <span class="type">int32_t</span> fd_table[MAX_FILES_OPEN_PER_PROC];      <span class="comment">//文件描述符数组</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">general_tag</span>;</span>       <span class="comment">//用于线程在一般队列的结点  </span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">all_list_tag</span>;</span>      <span class="comment">//用于线程队列thread_all_list的结点</span></span><br><span class="line">    <span class="type">uint32_t</span>* pgdir;                    <span class="comment">//进程自己的虚拟地址，为空则说明为内核线程 </span></span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">userprog_vaddr</span>;</span>                 <span class="comment">//用户进程的虚拟地址</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span> <span class="title">u_block_desc</span>[<span class="title">DESC_CNT</span>];</span>       <span class="comment">//用户进程内存块描述符</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> stack_magic;               <span class="comment">// 栈的边界标记，用于检测栈的溢出，注意为尾部</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.....</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.....</span></span><br><span class="line"><span class="comment">//初始化线程的基本信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_thread</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,<span class="type">char</span>*name,<span class="type">int</span> prio)</span></span><br><span class="line">{</span><br><span class="line">    <span class="built_in">memset</span>(pthread,<span class="number">0</span>,<span class="keyword">sizeof</span>(*pthread)); <span class="comment">//先清零所有内容</span></span><br><span class="line">    pthread-&gt;pid = allocate_pid();      <span class="comment">//获取pid</span></span><br><span class="line">    <span class="built_in">strcpy</span>(pthread-&gt;name,name);         <span class="comment">//名称</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//由于把main函数也封装成一个线程，并且也是一直运行的，所以直接设置成TASK_RUNNING,其余设置成就绪态</span></span><br><span class="line">    <span class="keyword">if</span>(pthread == main_thread)</span><br><span class="line">    {</span><br><span class="line">        pthread-&gt;status = TASK_RUNNING;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        pthread-&gt;status = TASK_READY;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">//self_kstack是线程自己内核态下使用的栈顶地址</span></span><br><span class="line">    pthread-&gt;self_kstack = (<span class="type">uint32_t</span>*)((<span class="type">uint32_t</span>)pthread + PG_SIZE);</span><br><span class="line">    pthread-&gt;priority = prio;           <span class="comment">//优先级</span></span><br><span class="line">    pthread-&gt;ticks = prio;              <span class="comment">//时间片</span></span><br><span class="line">    pthread-&gt;elapsed_ticks = <span class="number">0</span>;         <span class="comment">//0表示还没有开始运行</span></span><br><span class="line">    pthread-&gt;pgdir = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//预留标准输入输出(文件描述符数组)--0 是标准输入，1 是标准输出，2 是标准错误</span></span><br><span class="line">    pthread-&gt;fd_table[<span class="number">0</span>] = <span class="number">0</span>; </span><br><span class="line">    pthread-&gt;fd_table[<span class="number">1</span>] = <span class="number">1</span>; </span><br><span class="line">    pthread-&gt;fd_table[<span class="number">2</span>] = <span class="number">2</span>; </span><br><span class="line">    <span class="type">uint8_t</span> fd_idx = <span class="number">3</span>; </span><br><span class="line">    <span class="keyword">while</span> (fd_idx &lt; MAX_FILES_OPEN_PER_PROC)<span class="comment">//其余的全置为-1  </span></span><br><span class="line">    { </span><br><span class="line">        pthread-&gt;fd_table[fd_idx] = <span class="number">-1</span>; </span><br><span class="line">        fd_idx++; </span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    pthread-&gt;stack_magic = <span class="number">0x22332233</span>;  <span class="comment">//自定义的魔数，同时它也是栈的边界</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.....</span></span><br></pre></td></tr></table></figure></div><hr><h3 id="B-3-有点晕了咳咳（创建文件，打开-关闭文件-写入文件……-）"><a href="#B-3-有点晕了咳咳（创建文件，打开-关闭文件-写入文件……-）" class="headerlink" title="B.3 有点晕了咳咳（创建文件，打开/关闭文件,写入文件…….）"></a>B.3 有点晕了咳咳（创建文件，打开/关闭文件,写入文件…….）</h3><p>这里后面我将fs文件夹的文件都贴出来吧，因为确实看晕了，并且内容比较碎片，都是一点一点添加的内容，不好总结，大家可以自行看看书中内容理解</p><p>消化一段时间再继续学习吧，最近可能要做点别的东西………….</p><h2 id="C-系统交互"><a href="#C-系统交互" class="headerlink" title="C 系统交互"></a>C 系统交互</h2><h2 id="D-完结撒花"><a href="#D-完结撒花" class="headerlink" title="D 完结撒花"></a>D 完结撒花</h2><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li>[操作系统真象还原 (郑纲) (Z-Library)]   — 大家可以自己在网上查找相关资源</li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#x34;&#x36;&#55;&#x30;&#x32;&#x33;&#54;&#x32;&#64;&#113;&#x71;&#x2e;&#x63;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">这一章主要对《操作系统真相还原》的文件系统，系统交互相关，本篇文章主要用来记录学习过程中遇到的问题，以及相关知识概念，方便后来查询......</summary>
    
    
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/categories/OS/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/OS/Linux/"/>
    
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/tags/OS/"/>
    
    <category term="《真相还原》" scheme="https://blog.haozi-haozi.cn/tags/%E3%80%8A%E7%9C%9F%E7%9B%B8%E8%BF%98%E5%8E%9F%E3%80%8B/"/>
    
  </entry>
  
  <entry>
    <title>真象还原 --用户进程/完善内核/硬盘驱动 study(4)</title>
    <link href="https://blog.haozi-haozi.cn/2025/11/03/os_elephant_four/"/>
    <id>https://blog.haozi-haozi.cn/2025/11/03/os_elephant_four/</id>
    <published>2025-11-03T12:35:11.000Z</published>
    <updated>2026-03-24T08:59:17.263Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A 前情提要"></a>A 前情提要</h2><p>在此之前，第一部分我们完成了基础的<strong>环境配置，bochs配置</strong>，以及<strong>MBR,loader</strong>的的基础编写，成功的进入了<strong>保护模式</strong>并且开启了<strong>内存分页功能</strong></p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/21/os_elephant_one/">真象还原 –环境/准备 study(1)</a></li></ul></blockquote><p>第二部分完成了对<strong>内联汇编</strong>，<strong>中断初始化</strong>，<strong>定时器初始化</strong>，也实现了<em>基础打印函数</em>*</p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/24/os_elephant_two/">真象还原 –内核/中断 study(2)</a></li></ul></blockquote><p>第三部分完成了对<strong>内存管理</strong>，<strong>线程</strong>，<strong>同步</strong>，也实现了<em>基础生产者与消费者</em>*</p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/28/os_elephant_three/">真象还原 –内存管理/线程/同步 study(3)</a></li></ul></blockquote><p>这一部分主要学习<strong>用户进程/完善内核(系统调用，完善内存)/硬盘驱动</strong>相关内容</p><p><strong>ps:如果参考本系列文章来实操，需要结合《操作系统真象还原》一起观看，否则会缺失很多细节</strong></p><h2 id="B-用户进程"><a href="#B-用户进程" class="headerlink" title="B 用户进程"></a>B 用户进程</h2><p>这里概念就不多说了（大家可以自己查询TSS–任务状态段），真象还原是仿照Linux的任务切换方法，其中我们也会使用到TSS，但是只是希望为0特权级的任务提供栈</p><blockquote><p>Linux 为每个 CPU 创建一个 TSS，在各个 CPU 上的所有任务共享同一个 TSS，各 CPU 的 TR 寄存器保存各 CPU 上的 TSS，在用 ltr 指令加载 TSS 后，该 TR 寄存器永远指向同一个 TSS，之后再也不会重新加载 TSS。在进程切换时，只需要把 TSS 中的 SS0 及 esp0 更新为新任务的内核栈的段地址及栈指针</p></blockquote><p>Linux 中任务切换不使用 call 和 jmp 指令，这也避免了任务切换的低效</p><p>这里我们直接开始写代码吧</p><hr><h3 id="B-1-定义并初始化TSS"><a href="#B-1-定义并初始化TSS" class="headerlink" title="B.1 定义并初始化TSS"></a>B.1 定义并初始化TSS</h3><p>首先是在<code>global.h</code>中添加等会需要用到的属性</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/global.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __KERNEL_GLOBAL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __KERNEL_GLOBAL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// ----------------  GDT描述符属性  ----------------</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span>DESC_G_4K    1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span>DESC_D_32    1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_L     0<span class="comment">// 64位代码标记，此处标记为0便可。</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_AVL     0<span class="comment">// cpu不用此位，暂置为0  </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_P     1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_DPL_0   0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_DPL_1   1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_DPL_2   2</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_DPL_3   3</span></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">   代码段和数据段属于存储段，tss和各种门描述符属于系统段</span></span><br><span class="line"><span class="comment">   s为1时表示存储段,为0时表示系统段.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_S_CODE1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_S_DATADESC_S_CODE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_S_SYS0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_TYPE_CODE8<span class="comment">// x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0.  </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_TYPE_DATA  2<span class="comment">// x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0.</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_TYPE_TSS   9<span class="comment">// B位为0,不忙</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL0  0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL1  1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL2  2</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL3  3</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TI_GDT 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TI_LDT 1</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_CODE   ((1 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2) + RPL0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_DATA   ((2 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2) + RPL0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_STACK   SELECTOR_K_DATA </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_GS   ((3 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2) + RPL0)</span></span><br><span class="line"><span class="comment">/* 第3个段描述符是显存,第4个是tss */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_U_CODE   ((5 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2) + RPL3)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_U_DATA   ((6 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2) + RPL3)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_U_STACK   SELECTOR_U_DATA</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GDT_ATTR_HIGH ((DESC_G_4K &lt;&lt; 7) + (DESC_D_32 &lt;&lt; 6) + (DESC_L &lt;&lt; 5) + (DESC_AVL &lt;&lt; 4))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GDT_CODE_ATTR_LOW_DPL3 ((DESC_P &lt;&lt; 7) + (DESC_DPL_3 &lt;&lt; 5) + (DESC_S_CODE &lt;&lt; 4) + DESC_TYPE_CODE)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GDT_DATA_ATTR_LOW_DPL3 ((DESC_P &lt;&lt; 7) + (DESC_DPL_3 &lt;&lt; 5) + (DESC_S_DATA &lt;&lt; 4) + DESC_TYPE_DATA)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//---------------  TSS描述符属性  ------------</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TSS_DESC_D  0 </span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TSS_ATTR_HIGH ((DESC_G_4K &lt;&lt; 7) + (TSS_DESC_D &lt;&lt; 6) + (DESC_L &lt;&lt; 5) + (DESC_AVL &lt;&lt; 4) + 0x0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TSS_ATTR_LOW ((DESC_P &lt;&lt; 7) + (DESC_DPL_0 &lt;&lt; 5) + (DESC_S_SYS &lt;&lt; 4) + DESC_TYPE_TSS)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_TSS ((4 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2 ) + RPL0)</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">gdt_desc</span> {</span></span><br><span class="line">   <span class="type">uint16_t</span> limit_low_word;</span><br><span class="line">   <span class="type">uint16_t</span> base_low_word;</span><br><span class="line">   <span class="type">uint8_t</span>  base_mid_byte;</span><br><span class="line">   <span class="type">uint8_t</span>  attr_low_byte;</span><br><span class="line">   <span class="type">uint8_t</span>  limit_high_attr_high;</span><br><span class="line">   <span class="type">uint8_t</span>  base_high_byte;</span><br><span class="line">}; </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//--------------   IDT描述符属性  ------------</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_P 1 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_DPL0   0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_DPL3   3</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_32_TYPE     0xE   <span class="comment">// 32位的门</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_16_TYPE     0x6   <span class="comment">// 16位的门，不用，定义它只为和32位门区分</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_ATTR_DPL0  ((IDT_DESC_P &lt;&lt; 7) + (IDT_DESC_DPL0 &lt;&lt; 5) + IDT_DESC_32_TYPE)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_ATTR_DPL3  ((IDT_DESC_P &lt;&lt; 7) + (IDT_DESC_DPL3 &lt;&lt; 5) + IDT_DESC_32_TYPE)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_SIZE 4096</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>然后创建一个目录<code>userprog</code>,以后用来存放有关用户进程的文件,同理，添加一个Makefile，修改后和添加的文件放在下面了</p><p>下面主要是对tss的初始化</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/tss.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"tss.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 任务状态段tss结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">tss</span> {</span></span><br><span class="line">    <span class="type">uint32_t</span> backlink;</span><br><span class="line">    <span class="type">uint32_t</span>* esp0;</span><br><span class="line">    <span class="type">uint32_t</span> ss0;</span><br><span class="line">    <span class="type">uint32_t</span>* esp1;</span><br><span class="line">    <span class="type">uint32_t</span> ss1;</span><br><span class="line">    <span class="type">uint32_t</span>* esp2;</span><br><span class="line">    <span class="type">uint32_t</span> ss2;</span><br><span class="line">    <span class="type">uint32_t</span> cr3;</span><br><span class="line">    <span class="type">uint32_t</span> (*eip) (<span class="type">void</span>);</span><br><span class="line">    <span class="type">uint32_t</span> eflags;</span><br><span class="line">    <span class="type">uint32_t</span> eax;</span><br><span class="line">    <span class="type">uint32_t</span> ecx;</span><br><span class="line">    <span class="type">uint32_t</span> edx;</span><br><span class="line">    <span class="type">uint32_t</span> ebx;</span><br><span class="line">    <span class="type">uint32_t</span> esp;</span><br><span class="line">    <span class="type">uint32_t</span> ebp;</span><br><span class="line">    <span class="type">uint32_t</span> esi;</span><br><span class="line">    <span class="type">uint32_t</span> edi;</span><br><span class="line">    <span class="type">uint32_t</span> es;</span><br><span class="line">    <span class="type">uint32_t</span> cs;</span><br><span class="line">    <span class="type">uint32_t</span> ss;</span><br><span class="line">    <span class="type">uint32_t</span> ds;</span><br><span class="line">    <span class="type">uint32_t</span> fs;</span><br><span class="line">    <span class="type">uint32_t</span> gs;</span><br><span class="line">    <span class="type">uint32_t</span> ldt;</span><br><span class="line">    <span class="type">uint32_t</span> trace;</span><br><span class="line">    <span class="type">uint32_t</span> io_base;</span><br><span class="line">}; </span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">tss</span> <span class="title">tss</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 更新tss中esp0字段的值为pthread的0级线 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">update_tss_esp</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread)</span> </span><br><span class="line">{</span><br><span class="line">   tss.esp0 = (<span class="type">uint32_t</span>*)((<span class="type">uint32_t</span>)pthread + PG_SIZE);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 创建gdt描述符 */</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">struct</span> gdt_desc <span class="title function_">make_gdt_desc</span><span class="params">(<span class="type">uint32_t</span>* desc_addr, <span class="type">uint32_t</span> limit, <span class="type">uint8_t</span> attr_low, <span class="type">uint8_t</span> attr_high)</span> </span><br><span class="line">{</span><br><span class="line">   <span class="type">uint32_t</span> desc_base = (<span class="type">uint32_t</span>)desc_addr;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">gdt_desc</span> <span class="title">desc</span>;</span></span><br><span class="line">   desc.limit_low_word = limit &amp; <span class="number">0x0000ffff</span>;</span><br><span class="line">   desc.base_low_word = desc_base &amp; <span class="number">0x0000ffff</span>;</span><br><span class="line">   desc.base_mid_byte = ((desc_base &amp; <span class="number">0x00ff0000</span>) &gt;&gt; <span class="number">16</span>);</span><br><span class="line">   desc.attr_low_byte = (<span class="type">uint8_t</span>)(attr_low);</span><br><span class="line">   desc.limit_high_attr_high = (((limit &amp; <span class="number">0x000f0000</span>) &gt;&gt; <span class="number">16</span>) + (<span class="type">uint8_t</span>)(attr_high));</span><br><span class="line">   desc.base_high_byte = desc_base &gt;&gt; <span class="number">24</span>;</span><br><span class="line">   <span class="keyword">return</span> desc;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在gdt中创建tss并重新加载gdt */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">tss_init</span><span class="params">()</span> </span><br><span class="line">{</span><br><span class="line">   put_str(<span class="string">"tss_init start\n"</span>);</span><br><span class="line">   <span class="type">uint32_t</span> tss_size = <span class="keyword">sizeof</span>(tss);</span><br><span class="line">   <span class="built_in">memset</span>(&amp;tss, <span class="number">0</span>, tss_size);</span><br><span class="line">   tss.ss0 = SELECTOR_K_STACK;</span><br><span class="line">   tss.io_base = tss_size;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* gdt段基址为0x900,把tss放到第4个位置,也就是0x900+0x20的位置 */</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">/* 在gdt中添加dpl为0的TSS描述符 */</span></span><br><span class="line">  *((<span class="keyword">struct</span> gdt_desc*)<span class="number">0xc0000920</span>) = make_gdt_desc((<span class="type">uint32_t</span>*)&amp;tss, tss_size - <span class="number">1</span>, TSS_ATTR_LOW, TSS_ATTR_HIGH);</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* 在gdt中添加dpl为3的数据段和代码段描述符 */</span></span><br><span class="line">  *((<span class="keyword">struct</span> gdt_desc*)<span class="number">0xc0000928</span>) = make_gdt_desc((<span class="type">uint32_t</span>*)<span class="number">0</span>, <span class="number">0xfffff</span>, GDT_CODE_ATTR_LOW_DPL3, GDT_ATTR_HIGH);</span><br><span class="line">  *((<span class="keyword">struct</span> gdt_desc*)<span class="number">0xc0000930</span>) = make_gdt_desc((<span class="type">uint32_t</span>*)<span class="number">0</span>, <span class="number">0xfffff</span>, GDT_DATA_ATTR_LOW_DPL3, GDT_ATTR_HIGH);</span><br><span class="line">   </span><br><span class="line">  <span class="comment">/* gdt 16位的limit 32位的段基址 */</span></span><br><span class="line">   <span class="type">uint64_t</span> gdt_operand = ((<span class="number">8</span> * <span class="number">7</span> - <span class="number">1</span>) | ((<span class="type">uint64_t</span>)(<span class="type">uint32_t</span>)<span class="number">0xc0000900</span> &lt;&lt; <span class="number">16</span>));   <span class="comment">// 7个描述符大小</span></span><br><span class="line">   <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"lgdt %0"</span> : : <span class="string">"m"</span> (gdt_operand))</span>;</span><br><span class="line">   <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"ltr %w0"</span> : : <span class="string">"r"</span> (SELECTOR_TSS))</span>;</span><br><span class="line">   put_str(<span class="string">"tss_init and ltr done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/tss.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __USERPROG_TSS_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __USERPROG_TSS_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="type">void</span> <span class="title function_">update_tss_esp</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">tss_init</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>下面是对Makefile的修改，主要还是添加文件依赖，以及头文件包含等，<strong>同时修改了之前有个位置写错了(有关用户库和内核库的文件编译重复了)</strong></p><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  内核构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/kernel/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># .PHONY 标明伪目标,它代表了一个需要被执行的动作或任务，而非一个需要被生成的文件</span></span><br><span class="line"><span class="comment"># 可以在本文件所在目录下 使用make all 、 make clean 等命令执行一系列任务</span></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: all kernel user clean img dirs</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 基础路径  =======================================</span></span><br><span class="line"><span class="comment"># 其中的 ":=" 表示立即展开使用，即右边的变量值会被立刻赋值，"$" 可以引用之前创建的变量</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line">MOUSE_DIR := ..</span><br><span class="line">BUILD_DIR := <span class="variable">$(MOUSE_DIR)</span>/build</span><br><span class="line">BUILD_KERNEL_DIR := <span class="variable">$(BUILD_DIR)</span>/kernel</span><br><span class="line">BUILD_USER_DIR := <span class="variable">$(BUILD_DIR)</span>/user</span><br><span class="line">IMG_PATH := /home/mouse/OS_mouse/tool/bochs/hd60M.img</span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 编译工具  =======================================</span></span><br><span class="line">CC := gcc</span><br><span class="line">ASM := nasm</span><br><span class="line">LD := ld</span><br><span class="line"></span><br><span class="line"><span class="comment"># C编译器标志，这里指出输出32位，编译器不识别/使用内建函数，禁用栈保护机制，指定当前为独立式环境(标准库不存在)，</span></span><br><span class="line"><span class="comment"># -I 分别包含需要的头文件路径(共用库，内核库，用户库，内核，设备,线程，用户进程)</span></span><br><span class="line">CFLAGS := -m32 -fno-builtin -fno-stack-protector -ffreestanding \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/lib -I<span class="variable">$(MOUSE_DIR)</span>/lib/kernel -I<span class="variable">$(MOUSE_DIR)</span>/lib/user \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/kernel -I<span class="variable">$(MOUSE_DIR)</span>/device -I<span class="variable">$(MOUSE_DIR)</span>/thread -I<span class="variable">$(MOUSE_DIR)</span>/userprog -c</span><br><span class="line">ASMFLAGS := -f elf</span><br><span class="line">LDFLAGS := -m elf_i386 -Ttext 0xc0001500 -e main</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===================== 包含子Makefile，导入子模块源文件  ===============================</span></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/device/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/thread/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/userprog/Makefile</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============================== 本目录源文件  ===========================================</span></span><br><span class="line">KERNEL_SRCS := main.c init.c interrupt.c debug.c memory.c</span><br><span class="line">KERNEL_ASMS := kernel.S</span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 为各模块添加前缀路径  =======================================</span></span><br><span class="line"><span class="comment"># $(addprefix &lt;prefix&gt;,&lt;names&gt;)是一个内置函数，能将路径prefix添加到names前</span></span><br><span class="line"><span class="comment"># ======================================================================================</span></span><br><span class="line">KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_SRCS)</span>)</span></span><br><span class="line">KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_SRCS)</span>)</span></span><br><span class="line">LIB_KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_USER_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_SRCS)</span>)</span></span><br><span class="line">LIB_USER_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">DEVICE_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_SRCS)</span>)</span></span><br><span class="line">DEVICE_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_SRCS)</span>)</span></span><br><span class="line">LIB_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">THREAD_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/thread/,<span class="variable">$(THREAD_SRCS)</span>)</span></span><br><span class="line">THREAD_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/thread/,<span class="variable">$(THREAD_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">USERPROG_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/userprog/,<span class="variable">$(USERPROG_SRCS)</span>)</span></span><br><span class="line">USERPROG_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/userprog/,<span class="variable">$(USERPROG_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 汇总全部源文件  ================================================</span></span><br><span class="line">ALL_C_SRCS := <span class="variable">$(KERNEL_SRCS)</span> <span class="variable">$(LIB_KERNEL_SRCS)</span> <span class="variable">$(LIB_USER_SRCS)</span> <span class="variable">$(DEVICE_SRCS)</span> <span class="variable">$(LIB_SRCS)</span> <span class="variable">$(THREAD_SRCS)</span> <span class="variable">$(USERPROG_SRCS)</span></span><br><span class="line">ALL_ASMS   := <span class="variable">$(KERNEL_ASMS)</span> <span class="variable">$(LIB_KERNEL_ASMS)</span> <span class="variable">$(LIB_USER_ASMS)</span> <span class="variable">$(DEVICE_ASMS)</span> <span class="variable">$(LIB_ASMS)</span> <span class="variable">$(THREAD_ASMS)</span> <span class="variable">$(USERPROG_ASMS)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 生成对应的 .o 文件路径  ========================================</span></span><br><span class="line"><span class="comment"># $(filter &lt;pattern...&gt;,&lt;text&gt;)：这个函数用于从 &lt;text&gt;中筛选出符合模式 &lt;pattern&gt;的单词</span></span><br><span class="line"><span class="comment"># $(patsubst &lt;pattern&gt;,&lt;replacement&gt;,&lt;text&gt;)：将 &lt;text&gt;中所有匹配 &lt;pattern&gt;的单词替换为 </span></span><br><span class="line"><span class="comment"># &lt;replacement&gt;的形式，&lt;pattern&gt;中可以使用通配符 %</span></span><br><span class="line"><span class="comment"># 通过这个函数，将生成的.o文件分别生成到build路径下的不同路径(这里除了用户，都生成到/build/kernel)</span></span><br><span class="line"><span class="comment"># =========================================================================================</span></span><br><span class="line">OBJS_KERNEL := \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,$(<span class="built_in">filter</span>-out <span class="variable">$(MOUSE_DIR)</span>/lib/user/%,<span class="variable">$(ALL_C_SRCS)</span>)</span>)) \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,$(<span class="built_in">filter</span>-out <span class="variable">$(MOUSE_DIR)</span>/lib/user/%,<span class="variable">$(ALL_ASMS)</span>)</span>))</span><br><span class="line"></span><br><span class="line">OBJS_USER := \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c,<span class="variable">$(BUILD_USER_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c,<span class="variable">$(ALL_C_SRCS)</span>)</span>) \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S,<span class="variable">$(BUILD_USER_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S,<span class="variable">$(ALL_ASMS)</span>)</span>)</span><br><span class="line"></span><br><span class="line">KERNEL_BIN := <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line"></span><br><span class="line"><span class="comment"># ==================================== 主要规则  ==========================================</span></span><br><span class="line"><span class="section">all: dirs kernel user</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 以@开头的指令，终端只会显示命令的输出，不显示命令本身--这里创建build文件夹</span></span><br><span class="line"><span class="section">dirs:</span></span><br><span class="line">@echo <span class="string">"Creating build directories..."</span></span><br><span class="line">@mkdir -p <span class="variable">$(BUILD_KERNEL_DIR)</span> <span class="variable">$(BUILD_USER_DIR)</span></span><br><span class="line"></span><br><span class="line"><span class="section">kernel: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line"><span class="section">user: <span class="variable">$(OBJS_USER)</span></span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(KERNEL_BIN)</span>: <span class="variable">$(OBJS_KERNEL)</span> <span class="variable">$(OBJS_USER)</span></span><br><span class="line">@echo <span class="string">"Linking kernel object files to generate kernel.bin..."</span></span><br><span class="line"><span class="variable">$(LD)</span> <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,<span class="variable">$^</span>)</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_USER_DIR)</span>/%.o,<span class="variable">$^</span>)</span></span><br><span class="line">@echo <span class="string">"Kernel linking completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================= 编译规则 ============================================</span></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling user C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling user S: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/userprog/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling userprog C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/userprog/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling userprog S: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 镜像生成 ==========================================</span></span><br><span class="line"><span class="section">img: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line">@echo <span class="string">"Writing kernel image to disk..."</span></span><br><span class="line">dd if=<span class="variable">$(KERNEL_BIN)</span> of=<span class="variable">$(IMG_PATH)</span> bs=512 count=200 seek=9 conv=notrunc</span><br><span class="line">@echo <span class="string">"Kernel image writing completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 清理规则 ==========================================</span></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">@echo <span class="string">"Cleaning build files..."</span></span><br><span class="line">-rm -f <span class="variable">$(BUILD_KERNEL_DIR)</span>/*.o <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line">-rm -f <span class="variable">$(BUILD_USER_DIR)</span>/*.o</span><br><span class="line">@echo <span class="string">"Cleanup completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="section">mouse: clean all img</span></span><br><span class="line">@echo <span class="string">"Mouse build process (clean -&gt; all -&gt; img) completed!"</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#userprog/Makefile</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">USERPROG_SRCS := tss.c  process.c  <span class="comment"># (列出该目录下所有.c文件)</span></span><br><span class="line">USERPROG_ASMS :=  <span class="comment"># 例如: start.S (列出该目录下所有.S文件，如果没有则留空)</span></span><br></pre></td></tr></table></figure></div><p><strong>注意</strong>，<strong>要在init.c文件中添加函数<code>tss_init()</code></strong></p><p>这些准备工作更加偏向概念，这里就不多说了，大家可以仔细阅读一下原书内容，下面才正式开始实现用户进程</p><hr><h3 id="B-2-实现用户进程（1）"><a href="#B-2-实现用户进程（1）" class="headerlink" title="B.2 实现用户进程（1）"></a>B.2 实现用户进程（1）</h3><p>首先在要在进程的基础上来实现进程，我们先来大致了解一下原理，做点准备：</p><p>进程与内核线程最大的区别是进程有单独的 4GB 空间，这里指的是虚拟地址，每个进程都拥有 4GB 的虚拟地址空间，<strong>虚拟地址连续而物理地址可以不连续</strong>，这就是保护模式下分页机制的优势</p><blockquote><p>为演示此特性，我们需要单独为每个进程维护一个虚拟地址池，用此地址池来记录该进程的虚拟中，哪些已被分配，哪些可以分配。与各个进程相关的数据，如果数据量不大的话，最好是存储在<strong>该进程的 PCB 中</strong>，这样便于管理。在上一节中已经知道，进程是基于线程实现的，因此它和线程一样使用相同的 pcb 结构，即 struct task_struct，我们要做的就是<strong>在此结构中增加一个成员，用它来跟踪用户空间虚拟地址的分配情况</strong></p></blockquote><p>下面就来修改thread.h文件，这里给出修改的部分：</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//略.....</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.....</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进程或线程的 pcb，程序控制块 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span>* self_kstack;      <span class="comment">// 各内核线程都用自己的内核栈,注意为首位</span></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">task_status</span> <span class="title">status</span>;</span>    <span class="comment">// 任务状态</span></span><br><span class="line">    <span class="type">uint8_t</span> priority;           <span class="comment">// 线程优先级</span></span><br><span class="line">    <span class="type">char</span> name[<span class="number">16</span>];              <span class="comment">// 名称</span></span><br><span class="line">    <span class="type">uint8_t</span> ticks;              <span class="comment">// 每次在处理器上执行的时间滴答数,每次时钟中断都会对其减1，到0则换下处理器</span></span><br><span class="line">    <span class="type">uint32_t</span> elapsed_ticks;     <span class="comment">// 从cpu运行至今占用了多少cpu滴答数</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">general_tag</span>;</span>       <span class="comment">//用于线程在一般队列的结点  </span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">all_list_tag</span>;</span>      <span class="comment">//用于线程队列thread_all_list的结点</span></span><br><span class="line">    <span class="type">uint32_t</span>* pgdir;                    <span class="comment">//进程自己的虚拟地址     </span></span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">userprog_vaddr</span>;</span>     <span class="comment">//用户进程的虚拟地址</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> stack_magic;               <span class="comment">// 栈的边界标记，用于检测栈的溢出，注意为尾部</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.....</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后再<code>memory.c</code>中添加相关的功能，在内存池<code>struct pool</code>中<strong>添加一个锁</strong>用来在申请内存的时候作为互斥使用，后面添加了在<strong>用户内存池分配内存的函数</strong>等，以及<strong>让物理地址和虚拟地址建立映射</strong>，最后再添加一个<strong>通过虚拟地址返回物理地址的函数</strong>，记得还要在初始化函数中<strong>添加锁的初始化</strong>，这里还是给出片段，总体的代码我应该会添加到本节的最后：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存池结构,生成两个实例用于管理内核内存池和用户内存池 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> {</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">pool_bitmap</span>;</span>   <span class="comment">// 本内存池用到的位图结构,用于管理物理内存</span></span><br><span class="line">   <span class="type">uint32_t</span> phy_addr_start;    <span class="comment">// 本内存池所管理物理内存的起始地址</span></span><br><span class="line">   <span class="type">uint32_t</span> pool_size;    <span class="comment">// 本内存池字节容量</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">lock</span>;</span>            <span class="comment">// 申请内存的时候互斥</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在 pf 表示的虚拟内存池中申请 pg_cnt 个虚拟页，</span></span><br><span class="line"><span class="comment"> * 成功则返回虚拟页的起始地址，失败则返回 NULL */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span>* <span class="title function_">vaddr_get</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="type">uint32_t</span> pg_cnt)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> vaddr_start = <span class="number">0</span>, bit_idx_start = <span class="number">-1</span>; </span><br><span class="line">    <span class="type">uint32_t</span> cnt = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">if</span> (pf == PF_KERNEL)        <span class="comment">//内核内存池</span></span><br><span class="line">    { </span><br><span class="line">        bit_idx_start = bitmap_scan(&amp;kernel_vaddr.vaddr_bitmap, pg_cnt); </span><br><span class="line">        <span class="keyword">if</span> (bit_idx_start == <span class="number">-1</span>) </span><br><span class="line">        { </span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NULL</span>; </span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">while</span>(cnt &lt; pg_cnt) </span><br><span class="line">        { </span><br><span class="line">            bitmap_set(&amp;kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, <span class="number">1</span>); </span><br><span class="line">        } </span><br><span class="line">        vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE; </span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span>            <span class="comment">//用户内存池</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">struct</span> task_struct* cur = running_thread(); <span class="comment">//获取当前PCB</span></span><br><span class="line">        bit_idx_start = bitmap_scan(&amp;cur-&gt;userprog_vaddr.vaddr_bitmap,pg_cnt);  <span class="comment">//申请pg_cnt个位置</span></span><br><span class="line">        <span class="keyword">if</span>(bit_idx_start == <span class="number">-1</span>)</span><br><span class="line">        {</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">while</span>(cnt&lt;pg_cnt)</span><br><span class="line">        {</span><br><span class="line">            bitmap_set(&amp;cur-&gt;userprog_vaddr.vaddr_bitmap,bit_idx_start+cnt++,<span class="number">1</span>);    <span class="comment">//设置位图</span></span><br><span class="line">        }</span><br><span class="line">        vaddr_start = cur-&gt;userprog_vaddr.vaddr_start + bit_idx_start*PG_SIZE;</span><br><span class="line">        ASSERT((<span class="type">uint32_t</span>)vaddr_start &lt; (<span class="number">0xc0000000</span> - PG_SIZE)); <span class="comment">//(0xc0000000 - PG_SIZE)作为用户 3 级栈已经在 start_process 被分配</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">void</span>*)vaddr_start;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//在用户空间申请4K内存，并返回虚拟地址</span></span><br><span class="line"><span class="type">void</span>* <span class="title function_">get_user_pages</span><span class="params">(<span class="type">uint32_t</span> pg_cnt)</span></span><br><span class="line">{</span><br><span class="line">    lock_acquire(&amp;user_pool.lock);  <span class="comment">//锁</span></span><br><span class="line">    <span class="type">void</span>* vadder = malloc_page(PF_USER,pg_cnt);</span><br><span class="line">    <span class="built_in">memset</span>(vadder,<span class="number">0</span>,pg_cnt*PG_SIZE);</span><br><span class="line">    lock_release(&amp;user_pool.lock);</span><br><span class="line">    <span class="keyword">return</span> vadder;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//将地址vaddr与pf的物理地址关联，仅支持一页空间分配</span></span><br><span class="line"><span class="type">void</span>* <span class="title function_">get_a_page</span><span class="params">(<span class="keyword">enum</span> pool_flags pf,<span class="type">uint32_t</span> vaddr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span> =</span> pf &amp; PF_KERNEL? &amp;kernel_pool : &amp;user_pool;</span><br><span class="line">    lock_acquire(&amp;mem_pool-&gt;lock);</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> =</span> running_thread(); <span class="comment">//获取当前pcb</span></span><br><span class="line">    <span class="type">int32_t</span> bit_idx = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//若当前的用户进程申请用户内存，就修改用户进程自己的虚拟地址位图</span></span><br><span class="line">    <span class="keyword">if</span>(cur-&gt;pgdir != <span class="literal">NULL</span> &amp;&amp; pf == PF_USER)</span><br><span class="line">    {</span><br><span class="line">        bit_idx = (vaddr - cur-&gt;userprog_vaddr.vaddr_start) / PG_SIZE;</span><br><span class="line">        ASSERT(bit_idx &gt; <span class="number">0</span>);</span><br><span class="line">        bitmap_set(&amp;cur-&gt;userprog_vaddr.vaddr_bitmap,bit_idx,<span class="number">1</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">//如果是内核线程申请内核内存，就修改kernel_vaddr</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(cur-&gt;pgdir == <span class="literal">NULL</span> &amp; pf == PF_KERNEL)</span><br><span class="line">    {</span><br><span class="line">        bit_idx = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE; </span><br><span class="line">        ASSERT(bit_idx &gt; <span class="number">0</span>); </span><br><span class="line">        bitmap_set(&amp;kernel_vaddr.vaddr_bitmap, bit_idx, <span class="number">1</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        PANIC(<span class="string">"get_a_page:not allow kernel alloc userspace or user alloc kernelspace by get_a_page"</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="type">void</span>* page_phyaddr = palloc(mem_pool);  <span class="comment">//分配一个物理页，返回页框地址</span></span><br><span class="line">    <span class="keyword">if</span>(page_phyaddr == <span class="literal">NULL</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    }</span><br><span class="line">    page_table_add((<span class="type">void</span>*)vaddr, page_phyaddr); <span class="comment">//关联物理地址和虚拟地址</span></span><br><span class="line">    lock_release(&amp;mem_pool-&gt;lock);</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">void</span>*)vaddr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//得到虚拟地址映射到的物理地址</span></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">addr_v2p</span><span class="params">(<span class="type">uint32_t</span> vaddr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span>* pte = pte_ptr(vaddr); <span class="comment">//得到虚拟地址 vaddr 对应的 pte 指针</span></span><br><span class="line">    <span class="comment">//(*pte)是页表所在的物理页框地址，去掉低12位的页表项属性+虚拟地址vadder的低12位</span></span><br><span class="line">    <span class="keyword">return</span> ((*pte &amp; <span class="number">0xfffff000</span>) + (vaddr &amp; <span class="number">0x00000fff</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存管理部分初始化入口 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mem_init</span><span class="params">()</span> </span><br><span class="line">{</span><br><span class="line">   put_str(<span class="string">"mem_init start\n"</span>);</span><br><span class="line">   <span class="type">uint32_t</span> mem_bytes_total = (*(<span class="type">uint32_t</span>*)(<span class="number">0xb00</span>));</span><br><span class="line">   lock_init(&amp;kernel_pool.lock);    <span class="comment">//初始化锁</span></span><br><span class="line">   lock_init(&amp;user_pool.lock);</span><br><span class="line">   mem_pool_init(mem_bytes_total);  <span class="comment">// 初始化内存池</span></span><br><span class="line">   put_str(<span class="string">"mem_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>咳咳，后面还要继续修改memory.c函数…….</p><hr><h3 id="B-3-实现用户进程（2）"><a href="#B-3-实现用户进程（2）" class="headerlink" title="B.3 实现用户进程（2）"></a>B.3 实现用户进程（2）</h3><p>因为是用户进程，所以我肯定要进入特权级3，现在又该如何从搞特权级转入低优先级嘞</p><blockquote><p>，一般情况下，CPU 不允许从高特权级转向低特权级，除非是从中断和调用门返回的情况下。咱们系统中不打算使用调用门，因此，咱们进入特权级 3 只能借助从中断返回的方式，但用户进程还没有运行，何谈被中断？更谈不上从中断返回了……但是 CPU 比较呆头呆脑，我们可以骗过 CPU，在用户进程运行之前，使其以为我们在中断处理环境中，这样便“假装”从中断返回</p></blockquote><p>如何假装:<strong>首先得在特权级 0 的环境中，其次是执行 iretd 指令</strong></p><p>iretd 指令会用到栈中的数据作为返回地址，还会加载栈中 eflags的值到 eflags 寄存器，如果栈中 cs.rpl 若为更低的特权级，处理器的特权级检查通过后，会将栈中 cs 载入到 CS 寄存器，栈中 ss 载入 SS 寄存器，随后处理器进入低特权级，所以在栈中提前准备好数据即可</p><p>但是既然涉及到栈的操作了，那不如将进程的上下文也存到栈中，然后通过一系列的pop操作将用户进程的数据装载到寄存器，然后通过iretd指令退出中断，我们在退出中断的函数intr_exit中修改(kernel.S)</p><ul><li><strong>即便是假装，退出中断也要经过intr_exit</strong></li><li><strong>需要提前准备好栈，在里面装填用户进程的上下文，借用pop出栈的机会将上下文载入CPU寄存器</strong></li><li><strong>要在栈中存储CS选择子，将RPL修改为3(用户特权级)</strong></li><li><strong>栈中段寄存器的选择子必须指向 DPL 为 3 的内存段</strong> 用户进程只能访问DPL为3的内存段</li><li><strong>必须使栈中 eflags 的 IF 位为 1</strong> 响应新的中断</li><li><strong>必须使栈中 eflags 的 IOPL 位为 0</strong> IO操作</li></ul><p>下面来开始继续写代码，首先是global添加的定义</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//--------------   IDT描述符属性  ------------</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_P 1 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_DPL0   0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_DPL3   3</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_32_TYPE     0xE     <span class="comment">// 32位的门</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_16_TYPE     0x6     <span class="comment">// 16位的门，不用，定义它只为和32位门区分</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_ATTR_DPL0  ((IDT_DESC_P &lt;&lt; 7) + (IDT_DESC_DPL0 &lt;&lt; 5) + IDT_DESC_32_TYPE)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_ATTR_DPL3  ((IDT_DESC_P &lt;&lt; 7) + (IDT_DESC_DPL3 &lt;&lt; 5) + IDT_DESC_32_TYPE)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_MBS (1 &lt;&lt; 1)            <span class="comment">// 此项必须要设置</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_IF_1 (1 &lt;&lt; 9)           <span class="comment">// if 为 1，开中断</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_IF_0 0                  <span class="comment">// if 为 0，关中断</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_IOPL_3 (3 &lt;&lt; 12)        <span class="comment">//IOPL3，用于测试用户程序在非系统调用下进行 IO </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_IOPL_0 (0 &lt;&lt; 12)        <span class="comment">// IOPL0 </span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> NULL ((void*)0) </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DIV_ROUND_UP(X, STEP) ((X + STEP - 1) / (STEP)) </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> bool int </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> true 1 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> false 0 </span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_SIZE 4096</span></span><br></pre></td></tr></table></figure></div><p>现在还是在userprog目录下创建process.c/h来实现用户进程</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/process.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"tss.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="type">void</span> <span class="title function_">intr_exit</span><span class="params">(<span class="type">void</span>)</span>;        <span class="comment">//退出中断</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">thread_ready_list</span>;</span>      <span class="comment">//就绪队列</span></span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">thread_all_list</span>;</span>        <span class="comment">//所有任务队列</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//构建用户进程初始化上下文信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">start_process</span><span class="params">(<span class="type">void</span>* filename_ )</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">void</span>* function = filename_;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> =</span> running_thread(); <span class="comment">//当前任务PCB</span></span><br><span class="line">    cur-&gt;self_kstack += <span class="keyword">sizeof</span>(<span class="keyword">struct</span> thread_stack);  </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">intr_stack</span>* <span class="title">proc_stack</span> =</span> (<span class="keyword">struct</span> intr_stack*)cur-&gt;self_kstack;</span><br><span class="line">    proc_stack-&gt;edi = proc_stack-&gt;esi = proc_stack-&gt;ebp = proc_stack-&gt;esp_dummy= <span class="number">0</span>;</span><br><span class="line">    proc_stack-&gt;ebx = proc_stack-&gt;edx = proc_stack-&gt;ecx = proc_stack-&gt;eax = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    proc_stack-&gt;gs = <span class="number">0</span>; <span class="comment">//用户态用不上，初始化为0</span></span><br><span class="line">    proc_stack-&gt;ds = proc_stack-&gt;es = proc_stack-&gt;fs = SELECTOR_U_DATA;</span><br><span class="line">    proc_stack-&gt;eip = function;     <span class="comment">//待执行的用户程序地址</span></span><br><span class="line">    proc_stack-&gt;cs = SELECTOR_U_CODE;   </span><br><span class="line">    proc_stack-&gt;eflags = (EFLAGS_IOPL_0 | EFLAGS_MBS | EFLAGS_IF_1);</span><br><span class="line">    proc_stack-&gt;esp = (<span class="type">void</span>*)((<span class="type">uint32_t</span>)get_a_page(PF_USER,USER_STACK3_VADDR) + PG_SIZE );</span><br><span class="line">    proc_stack-&gt;ss = SELECTOR_U_DATA;</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"movl %0,%%esp; jmp intr_exit"</span>: : <span class="string">"g"</span> (proc_stack) : <span class="string">"memory"</span>)</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//激活页表</span></span><br><span class="line"><span class="comment">/********************************************************</span></span><br><span class="line"><span class="comment"> * 执行此函数时,当前任务可能是线程。</span></span><br><span class="line"><span class="comment"> * 之所以对线程也要重新安装页表, 原因是上一次被调度的可能是进程,</span></span><br><span class="line"><span class="comment"> * 否则不恢复页表的话,线程就会使用进程的页表了。</span></span><br><span class="line"><span class="comment"> ********************************************************/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">page_dir_activate</span><span class="params">(<span class="keyword">struct</span> task_struct* p_thread)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//若为内核线程,则需要重新填充页表为0x100000</span></span><br><span class="line">    <span class="type">uint32_t</span> pagedir_phy_addr = <span class="number">0x100000</span>;  <span class="comment">// 默认为内核的页目录物理地址,也就是内核线程所用的页目录表</span></span><br><span class="line">    <span class="keyword">if</span>(p_thread-&gt;pgdir != <span class="literal">NULL</span>)     <span class="comment">//用户态进行有自己的页目录表</span></span><br><span class="line">    {</span><br><span class="line">        pagedir_phy_addr = addr_v2p((<span class="type">uint32_t</span>)p_thread-&gt;pgdir);     <span class="comment">//获取物理地址</span></span><br><span class="line">    }</span><br><span class="line">   <span class="comment">//更新页目录寄存器cr3,使新页表生效</span></span><br><span class="line">   <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"movl %0, %%cr3"</span> : : <span class="string">"r"</span> (pagedir_phy_addr) : <span class="string">"memory"</span>)</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 激活线程或进程的页表,更新tss中的esp0为进程的特权级0的栈</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">process_activate</span><span class="params">(<span class="keyword">struct</span> task_struct* p_thread)</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(p_thread != <span class="literal">NULL</span>);</span><br><span class="line">    page_dir_activate(p_thread);    <span class="comment">//激活页表</span></span><br><span class="line">    <span class="comment">//内核线程特权级本身就是0,处理器进入中断时并不会从tss中获取0特权级栈地址,故不需要更新esp0</span></span><br><span class="line">    <span class="keyword">if</span>(p_thread-&gt;pgdir)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">//更新该进程的esp0,用于进程被中断时候保留上下文</span></span><br><span class="line">        update_tss_esp(p_thread);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建页目录表,将当前页表的表示内核空间的pde复制,</span></span><br><span class="line"><span class="comment">//成功则返回页目录的虚拟地址,否则返回-1 */</span></span><br><span class="line"><span class="type">uint32_t</span>* <span class="title function_">create_page_dir</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//用户进程的页表不能让用户直接访问到,所以在内核空间来申请</span></span><br><span class="line">    <span class="type">uint32_t</span>* page_dir_vaddr = get_kernel_pages(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span>(page_dir_activate == <span class="literal">NULL</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(<span class="string">"create_page_dir: get_kernel_page failed!"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">//1. 先复制页表   page_dir_vaddr + 0x300*4 是内核页目录的第768项</span></span><br><span class="line">    <span class="built_in">memcpy</span>((<span class="type">uint32_t</span>*)((<span class="type">uint32_t</span>)page_dir_vaddr + <span class="number">0x300</span>*<span class="number">4</span>), (<span class="type">uint32_t</span>*)(<span class="number">0xfffff000</span>+<span class="number">0x300</span>*<span class="number">4</span>), <span class="number">1024</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//2. 更新页目录地址</span></span><br><span class="line">    <span class="type">uint32_t</span> new_page_dir_phy_addr = addr_v2p((<span class="type">uint32_t</span>)page_dir_vaddr);</span><br><span class="line">    page_dir_vaddr[<span class="number">1023</span>] = new_page_dir_phy_addr | PG_US_U | PG_RW_W | PG_P_1;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> page_dir_vaddr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建用户进程虚拟地址位图</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">create_user_vaddr_bitmap</span><span class="params">(<span class="keyword">struct</span> task_struct* user_prog)</span></span><br><span class="line">{</span><br><span class="line">    user_prog-&gt;userprog_vaddr.vaddr_start = USER_VADDR_START;</span><br><span class="line">    <span class="type">uint32_t</span> bitmap_pg_cnt = DIV_ROUND_UP((<span class="number">0xc0000000</span> - USER_VADDR_START) / PG_SIZE / <span class="number">8</span> , PG_SIZE);</span><br><span class="line">    user_prog-&gt;userprog_vaddr.vaddr_bitmap.bits = get_kernel_pages(bitmap_pg_cnt);</span><br><span class="line">    user_prog-&gt;userprog_vaddr.vaddr_bitmap.btmp_bytes_len = (<span class="number">0xc0000000</span> - USER_VADDR_START) / PG_SIZE / <span class="number">8</span>;</span><br><span class="line">    bitmap_init(&amp;user_prog-&gt;userprog_vaddr.vaddr_bitmap);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建用户进程</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">process_execute</span><span class="params">(<span class="type">void</span>* filename,<span class="type">char</span>*name)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//pcb内核的数据结构,由内核来维护进程信息,因此要在内核内存池中申请</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">thread</span> =</span> get_kernel_pages(<span class="number">1</span>);</span><br><span class="line">    init_thread(thread,name,default_prio);  <span class="comment">//默认优先级31</span></span><br><span class="line">    create_user_vaddr_bitmap(thread);               <span class="comment">//创建位图</span></span><br><span class="line">    thread_create(thread,start_process,filename);   <span class="comment">//通过start_process函数退出中断进入用户进程</span></span><br><span class="line">    thread-&gt;pgdir = create_page_dir();              <span class="comment">//创建页目录表</span></span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable(); </span><br><span class="line"></span><br><span class="line">    ASSERT(!elem_find(&amp;thread_ready_list, &amp;thread-&gt;general_tag));</span><br><span class="line">    list_append(&amp;thread_ready_list, &amp;thread-&gt;general_tag);</span><br><span class="line">    ASSERT(!elem_find(&amp;thread_all_list, &amp;thread-&gt;all_list_tag));</span><br><span class="line">    list_append(&amp;thread_all_list, &amp;thread-&gt;all_list_tag);</span><br><span class="line"></span><br><span class="line">    intr_set_status(old_status);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/process.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __USERPROG_PROCESS_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __USERPROG_PROCESS_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> default_prio 31</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> USER_STACK3_VADDR  (0xc0000000 - 0x1000)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> USER_VADDR_START 0x8048000</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">process_execute</span><span class="params">(<span class="type">void</span>* filename, <span class="type">char</span>* name)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">start_process</span><span class="params">(<span class="type">void</span>* filename_)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">process_activate</span><span class="params">(<span class="keyword">struct</span> task_struct* p_thread)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">page_dir_activate</span><span class="params">(<span class="keyword">struct</span> task_struct* p_thread)</span>;</span><br><span class="line"><span class="type">uint32_t</span>* <span class="title function_">create_page_dir</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">create_user_vaddr_bitmap</span><span class="params">(<span class="keyword">struct</span> task_struct* user_prog)</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>同理要在调度器中更新对进程的处理:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......................</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......................</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//任务调度器</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">schedule</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line">    </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> =</span> running_thread(); <span class="comment">//获取当前进程PCB</span></span><br><span class="line">    <span class="keyword">if</span>(cur-&gt;status == TASK_RUNNING)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">//若这个线程知识cpu时间片到了，就加入到就绪队列尾</span></span><br><span class="line">        ASSERT(!elem_find(&amp;thread_ready_list,&amp;cur-&gt;general_tag));</span><br><span class="line">        list_append(&amp;thread_ready_list,&amp;cur-&gt;general_tag);</span><br><span class="line">        cur-&gt;ticks = cur-&gt;priority; <span class="comment">//重新设置ticks为priority</span></span><br><span class="line">        cur-&gt;status = TASK_READY;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        <span class="comment">//什么都不用干，因为已经不在运行了，并且当前线程也不在就绪队列中</span></span><br><span class="line">        <span class="comment">//只用取出就绪列表的下一个任务即可</span></span><br><span class="line">    }</span><br><span class="line">    ASSERT(!list_empty(&amp;thread_ready_list));    <span class="comment">//就绪列表不为空</span></span><br><span class="line">    thread_tag = <span class="literal">NULL</span>;                          <span class="comment">//thread_tag清空</span></span><br><span class="line">    <span class="comment">//准备将就绪队列中的第一个就绪线程弹出，并放入CPU</span></span><br><span class="line">    thread_tag = list_pop(&amp;thread_ready_list);</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">next</span> =</span> elem2entry(<span class="keyword">struct</span> task_struct,general_tag,thread_tag);   <span class="comment">//寻找弹出的就绪队列的PCB结构体头</span></span><br><span class="line">    next-&gt;status = TASK_RUNNING;</span><br><span class="line"></span><br><span class="line">    process_activate(next); <span class="comment">//激活页表等</span></span><br><span class="line"></span><br><span class="line">    switch_to(cur,next);    <span class="comment">//切换进程</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......................</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-4-测试用户进程"><a href="#B-4-测试用户进程" class="headerlink" title="B.4 测试用户进程"></a>B.4 测试用户进程</h3><p>还是直接在main函数中测试,因为还没有实现根文件系统等，所以这里还是通过在主函数 添加函数来模拟用户级的进程(线程):</p><p>同时，因为用户级特权不能直接使用打印的函数，所以这里我们只修改变量，然后让内核线程来打印</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ioqueue.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"keyboard.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> test_var_a = <span class="number">0</span>, test_var_b = <span class="number">0</span>; <span class="comment">//全局变量</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有</span></span><br><span class="line">    <span class="comment">//创建一个线程，线程名，优先级，线程函数名，参数</span></span><br><span class="line">    thread_start(<span class="string">"k_thread_one"</span>,<span class="number">8</span>,k_thread_one,<span class="string">"A__:"</span>);   </span><br><span class="line">    thread_start(<span class="string">"k_thread_two"</span>,<span class="number">8</span>,k_thread_two,<span class="string">"B__:"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建一个用户进程，进程函数，进程名</span></span><br><span class="line">    process_execute(u_prog_a,<span class="string">"user_prog_a"</span>);</span><br><span class="line">    process_execute(u_prog_b,<span class="string">"user_prog_b"</span>);</span><br><span class="line"></span><br><span class="line">    intr_enable();      <span class="comment">//打开中断，使时钟中断起作用</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        ;</span><br><span class="line">    }   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(<span class="string">" v_a:0x"</span>); </span><br><span class="line">        console_put_int(test_var_a);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(<span class="string">" v_b:0x"</span>); </span><br><span class="line">        console_put_int(test_var_b);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        test_var_a++;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        test_var_b++;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在调试之前，补充一个问题,就是之前在<code>loader.S</code>文件，当我们进入保护模式创建页表之后，我通过<code>info gdt</code>会发现基地址并不是预期的<code>0x900</code>,而是<code>0x903</code>，原因已经补充到了当时出现问题的地方</p><blockquote><p><strong>如果loader.S开头运用了jmp loader_start这一命令，gdt表的基地址就会发生偏移，因为跳转指令本身可能占有3字节，可能会往后偏移，导致后面的tss添加的时候不能以0x900作为gdt基地址</strong></p></blockquote><p>所以之前在初始化tss的时候，地址发生了错误，这样编译运行便会发生报错</p><p>这里有两个修改方案，第一个就是<strong>不使用<code>jmp loader_start</code>指令，让基地址为正常值,同时修改mbr文件中跳转的命令，让其可以正常跳转到<code>loader_start</code>的位置</strong>，第二个就是修改初始化tss的时候使用的基地址(先通过info gdt得到真正的基地址，但是这样肯定不推荐的)</p><p>其中跳转+0x300，是通过计算得出的，最开始是填充偏移到0x200，然后又添加了256字节的数据区，累计起来就0x300</p><p>下面给出第一种方案的修改部分,注意修改之后还要重新编译写入磁盘</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> loader vstart=LOADER_BASE_ADDR</span><br><span class="line"><span class="comment">;LOADER_STACK_TOP equ LOADER_BASE_ADDR</span></span><br><span class="line"><span class="comment">;jmp loader_start</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 构建gdt及其内部的描述符,拆分为上4字节，下4字节</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">GDT_BASE:</span>   <span class="built_in">dd</span> <span class="number">0x00000000</span>       <span class="comment">;空描述段</span></span><br><span class="line">            <span class="built_in">dd</span> <span class="number">0x00000000</span>       </span><br><span class="line"><span class="symbol">CODE_DESC:</span>  <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">            <span class="built_in">dd</span> DESC_CODE_HIGH4    </span><br><span class="line"><span class="symbol">DATA_STACK_DESC:</span>    <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">                    <span class="built_in">dd</span> DESC_DATA_HIGH4 </span><br><span class="line"><span class="symbol">VIDEO_DESC:</span> <span class="built_in">dd</span> <span class="number">0x80000007</span>               <span class="comment">;limit=(0xbffff-0xb8000)/4k=0x7 </span></span><br><span class="line">            <span class="built_in">dd</span> DESC_VIDEO_HIGH4         <span class="comment">;此时 dpl 为 0 </span></span><br><span class="line">GDT_SIZE    <span class="built_in">equ</span> $ - GDT_BASE </span><br><span class="line">GDT_LIMIT   <span class="built_in">equ</span> GDT_SIZE - <span class="number">1</span> </span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">60</span> <span class="built_in">dq</span> <span class="number">0</span> <span class="comment">; 此处预留 50 个描述符的空位(为什么不是60？因为我开头调用jmp，使得total_mem_bytes无法刚好是0xb00)  times 是 nasm 提供的伪指令，用来重复执行 times 后面表达式(编译器执行)</span></span><br><span class="line"></span><br><span class="line">SELECTOR_CODE <span class="built_in">equ</span> (<span class="number">0x0001</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 </span><br><span class="line"> <span class="comment">; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 </span></span><br><span class="line">SELECTOR_DATA <span class="built_in">equ</span> (<span class="number">0x0002</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line">SELECTOR_VIDEO <span class="built_in">equ</span> (<span class="number">0x0003</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;times 0x200 - ($ -$$) db 0  ;填充0，使得total_mem_bytes 的节内偏移一定为0x200,地址一定为0xb00</span></span><br><span class="line"></span><br><span class="line">total_mem_bytes <span class="built_in">dd</span> <span class="number">0</span></span><br><span class="line"><span class="comment">; total_mem_bytes 用于保存内存容量，以字节为单位，这个位置比价好记</span></span><br><span class="line"><span class="comment">; 当前偏移loader.bin文件头0x200 字节</span></span><br><span class="line"><span class="comment">; loader.bin加载地址为 0x900</span></span><br><span class="line"><span class="comment">; 所以 total_mem_bytes 内存地址为 0xb00</span></span><br><span class="line"><span class="comment">; 将来在内核中我们会引用这个地址，同时等会验证内存容量的时候，GDB调试也可以通过读取这个地址的内容来查看大小</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;以下是 gdt 的指针，前 2 字节是 gdt 界限，后 4 字节是 gdt 起始地址</span></span><br><span class="line"><span class="meta">align</span> <span class="number">4</span>     <span class="comment">;强制对齐</span></span><br><span class="line"><span class="symbol">gdt_ptr:</span> </span><br><span class="line">        <span class="built_in">dw</span> GDT_LIMIT </span><br><span class="line">        <span class="built_in">dd</span> GDT_BASE </span><br><span class="line">        loadermsg <span class="built_in">db</span> <span class="string">'Mosue'</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;人工对齐计算大小:total_mem_bytes(4) + gdt_ptr(6) + ards_buf(239) + ards_nr(2) + loadermsg（5） = 256字节</span></span><br><span class="line">ards_buf <span class="built_in">times</span> <span class="number">239</span> <span class="built_in">db</span> <span class="number">0</span>     <span class="comment">;填充对齐使用</span></span><br><span class="line">ards_nr <span class="built_in">dw</span> <span class="number">0</span>                <span class="comment">;用来记录ARDS结构体数量</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">loader_start:</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;略.............................</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/mbr.S</span></span><br><span class="line"><span class="comment">;主引导程序</span></span><br><span class="line"><span class="comment">; </span></span><br><span class="line"><span class="comment">;LOADER_BASE_ADDR equ 0xA000 </span></span><br><span class="line"><span class="comment">;LOADER_START_SECTOR equ 0x2 这里的内容添加在了boot.inc中,下面的%include引用</span></span><br><span class="line"><span class="comment">;------------------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">SECTION</span> MBR vstart=<span class="number">0x7c00</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">cs</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ds</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">es</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ss</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">fs</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">sp</span>,<span class="number">0x7c00</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="number">0xb800</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">gs</span>,<span class="built_in">ax</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;清屏</span></span><br><span class="line"><span class="comment">;利用 0x06 号功能，上卷全部行，则可清屏</span></span><br><span class="line"><span class="comment">; ----------------------------------------------------------- </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号：0x06 功能描述：上卷窗口</span></span><br><span class="line"><span class="comment">;------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入：</span></span><br><span class="line"><span class="comment">;AH 功能号= 0x06 </span></span><br><span class="line"><span class="comment">;AL = 上卷的行数（如果为 0，表示全部）</span></span><br><span class="line"><span class="comment">;BH = 上卷行属性</span></span><br><span class="line"><span class="comment">;(CL,CH) = 窗口左上角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;(DL,DH) = 窗口右下角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;无返回值: </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0600h</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0700h</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">0</span> <span class="comment">; 左上角: (0, 0) </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">184fh</span> <span class="comment">; 右下角: (80,25), </span></span><br><span class="line"><span class="comment">; VGA 文本模式中，一行只能容纳 80 个字符，共 25 行</span></span><br><span class="line"><span class="comment">; 下标从 0 开始，所以 0x18=24，0x4f=79 </span></span><br><span class="line"><span class="keyword">int</span> <span class="number">10h</span> <span class="comment">; int 10h </span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 输出背景色绿色，前景色红色，并且跳动的字符串"1 MBR" </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x00</span>],<span class="string">'1'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x01</span>],<span class="number">0xA4</span> <span class="comment">; A 表示绿色背景闪烁，4 表示前景色为红色</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x02</span>],<span class="string">' '</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x03</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x04</span>],<span class="string">'M'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x05</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x06</span>],<span class="string">'B'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x07</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x08</span>],<span class="string">'R'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x09</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">; 这里是主要添加的内容</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>,LOADER_START_SECTOR     <span class="comment">; 起始扇区 lba 地址</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>,LOADER_BASE_ADDR         <span class="comment">; 写入的地址</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">8</span>                        <span class="comment">; 待读入的扇区数，这里1即可</span></span><br><span class="line"><span class="keyword">call</span> rd_disk_m_16               <span class="comment">; 以下读取程序的起始部分(一个扇区) </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">jmp</span> LOADER_BASE_ADDR + <span class="number">0x300</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;略......................................</span></span><br></pre></td></tr></table></figure></div><p>然后编译写入运行，就可以得到书中的效果，即数字一直增大,下面我们来验证是否真的是用户特权级3</p><p>首先通过指令获取地址</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nm build/kernel/kernel.bin |grep -P <span class="string">'u_prog|test_var'</span></span><br></pre></td></tr></table></figure></div><p><code>mouse@ubuntu:~/OS_mouse/tool/bochs/mouse$ nm build/kernel/kernel.bin |grep -P 'u_prog|test_var'</code><br><code>c0007300 B test_var_a</code><br><code>c0007304 B test_var_b</code><br><code>c00015e7 T u_prog_a</code><br><code>c00015f9 T u_prog_b</code></p><p>得到变量的地址后，我们设置变量<code>u_prog_a</code>的断点，然后查看cs寄存器的值</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">lb 0xc00015e7     <span class="comment">#设置断点</span></span><br><span class="line"></span><br><span class="line">c               <span class="comment">#继续运行</span></span><br><span class="line"></span><br><span class="line">sreg            <span class="comment">#查看寄存器</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">&lt;bochs:1&gt; lb 0xc00015e7</span><br><span class="line">&lt;bochs:2&gt; c</span><br><span class="line">(0) Breakpoint 1, 0xc00015e7 <span class="keyword">in</span> ?? ()</span><br><span class="line">Next at t=22335944</span><br><span class="line">(0) [0x0000000015e7] 002b:c00015e7 (unk. ctxt): push ebp                  ; 55</span><br><span class="line">&lt;bochs:3&gt; sreg</span><br><span class="line">es:0x0033, dh=0x00cff300, dl=0x0000ffff, valid=1</span><br><span class="line">Data segment, base=0x00000000, <span class="built_in">limit</span>=0xffffffff, Read/Write, Accessed</span><br><span class="line">cs:0x002b, dh=0x00cff900, dl=0x0000ffff, valid=1</span><br><span class="line">Code segment, base=0x00000000, <span class="built_in">limit</span>=0xffffffff, Execute-Only, Non-Conforming, Accessed, 32-bit</span><br><span class="line">ss:0x0033, dh=0x00cff300, dl=0x0000ffff, valid=1</span><br><span class="line">Data segment, base=0x00000000, <span class="built_in">limit</span>=0xffffffff, Read/Write, Accessed</span><br><span class="line">ds:0x0033, dh=0x00cff300, dl=0x0000ffff, valid=1</span><br><span class="line">Data segment, base=0x00000000, <span class="built_in">limit</span>=0xffffffff, Read/Write, Accessed</span><br><span class="line">fs:0x0033, dh=0x00cff300, dl=0x0000ffff, valid=1</span><br><span class="line">Data segment, base=0x00000000, <span class="built_in">limit</span>=0xffffffff, Read/Write, Accessed</span><br><span class="line">gs:0x0000, dh=0x00001000, dl=0x00000000, valid=0</span><br><span class="line">ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1</span><br><span class="line"><span class="built_in">tr</span>:0x0020, dh=0xc0808b00, dl=0x7500006b, valid=1</span><br><span class="line">gdtr:base=0xc0000900, <span class="built_in">limit</span>=0x37</span><br><span class="line">idtr:base=0xc0007320, <span class="built_in">limit</span>=0x197</span><br><span class="line">&lt;bochs:4&gt; </span><br></pre></td></tr></table></figure></div><p>我们通过调试来设置断点，查看cs寄存器的值，此时 cs 的值为 0x002b，我们关注最低 4 位，其值为 b，换为二进制是 1011，最低 2 位为 rpl，也就是 3，所以可以判断此时用户进程确实是在 3 特权级下，与预期符合</p><p>ok呀，终于结束了，代码一多好难找调试找错误啊…….</p><hr><h2 id="C-完善内核-系统调用，完善内存"><a href="#C-完善内核-系统调用，完善内存" class="headerlink" title="C 完善内核(系统调用，完善内存)"></a>C 完善内核(系统调用，完善内存)</h2><p>之前我们用过系统调用，现在我们要自己来实现了</p><h3 id="C-1-系统调用简介"><a href="#C-1-系统调用简介" class="headerlink" title="C.1 系统调用简介"></a>C.1 系统调用简介</h3><p>系统调用就是让用户进程申请操作系统的帮助，让操作系统帮其完成某项工作，也就是相当于用户进程调用了操作系统的功能,我们还是参照 Linux 系统调用的原理，模仿着它咱们实现一份简易的系统调用版本</p><p>下面还是引用部分书中的句子</p><blockquote><p>Linux 系统调用是用中断门来实现的，通过软中断指令 int 来主动发起中断信号。由于要支持的系统功能很多，总不能一个系统功能调用就占用一个中断向量，真要是这样的话整个中断描述符表都不够用呢。Linux 只占用一个中断向量号，即 0x80，处理器执行指令 int 0x80 时便触发了系统调用。为了让用户程序可以通过这一个中断门调用多种系统功能，在系统调用前，Linux 在寄存器 eax 中写入子功能号，例如系统调用 open 和 close 都是不同的子功能号，当用户程序通过int 0x80 进行系统调用时，对应的中断处理例程会根据eax 的值来判断用户进程申请哪种系统调用</p></blockquote><p>Linux中使用的是系统调用 syscall，原型是 int syscall(int number, …)，其中的number 是 int 型，这是系统调用号，也就是前面所说的子功能号。不同的子功能需要的参数也是不同的，所以number 后面的“…”表示此函数支持变参，函数 syscall支持不同参数个数的系统调用，在新版本 Linux 中，所有的系统调用功能都可通过这一个函数完成。顺便说一句，函数 syscall 并不是由操作系统提供的，它是由 C 运行库 glibc（GNU 发布的 libc 库版本）提供的，因此 syscall实际上是库函数</p><p>书中给出了具体的例子，来讲解这个函数，库函数的syscall是简介使用系统调用的方式，那么肯定有直接使用的方式，也就是操作系统提供的<code>_syscall</code>,但是它已经被linux废弃了(注意，只是废弃了_syscall 这<br>个符号)，因为此方式最多支持6个参数,但是它的实现方式和思路非常简单，我们的简易操作系统就是使用的它</p><p>下面咱们拿<code>_syscall3</code>举例,当然还有其他的类型(_syscall是系统调用”族，原型是<code>_syscallX(type,name,type1,arg1,type2,arg2,…)</code></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \</span></span><br><span class="line"><span class="meta">type name(type1 arg1, type2 arg2, type3 arg3) { \</span></span><br><span class="line"><span class="meta">long __res; \</span></span><br><span class="line"><span class="meta">__asm__ volatile (<span class="string">"push %%ebx; movl %2,%%ebx; int $0x80; pop %%ebx"</span> \</span></span><br><span class="line"><span class="meta">: <span class="string">"=a"</span> (__res) \</span></span><br><span class="line"><span class="meta">: <span class="string">"0"</span> (__NR_##name),<span class="string">"ri"</span> ((long)(arg1)),<span class="string">"c"</span> ((long)(arg2)), \</span></span><br><span class="line"><span class="meta"><span class="string">"d"</span> ((long)(arg3)) : <span class="string">"memory"</span>); \</span></span><br><span class="line"><span class="meta">__syscall_return(type,__res); \</span></span><br><span class="line"><span class="meta">} </span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __syscall_return(type, res) \</span></span><br><span class="line"><span class="meta">do { \</span></span><br><span class="line"><span class="meta"><span class="keyword">if</span> ((unsigned long)(res) &gt;= (unsigned long)(-125)) { \</span></span><br><span class="line"><span class="meta">errno = -(res); \</span></span><br><span class="line"><span class="meta">res = -1; \</span></span><br><span class="line"><span class="meta">} \</span></span><br><span class="line"><span class="meta">return (type) (res); \</span></span><br><span class="line"><span class="meta">} while (0) </span></span><br></pre></td></tr></table></figure></div><blockquote><p>这里给大伙说明下，Linux 中的系统调用是用寄存器来传递参数的，这些参数需要按照从左到右的顺序依次存入到不同的通用寄存器（除 esp）中。其中，寄存器 eax 用来保存子功能号，ebx 保存第 1 个参数，ecx 保存第 2 个参数，edx 保存第 3 个参数，esi 保存第 4 个参数，edi 保存第 5 个参数。传递参数还可以用栈（内存）;<br>不知道您想过没有,为什么 Linux 用寄存器来传递参数，而不用栈？用寄存器快？肯定是这样的，没有哪个操作系统愿意更慢。不过这个“快”可不是出于存储介质方面的考虑，而是用寄存器传参的步骤少一些，听我慢慢道来。用户进程执行 int 0x80 时还处于用户态，编译器根据 c 调用约定，系统调用所用的参数会被压到用户栈中，这是 3 特权级栈。当 int 0x80 执行后，任务陷入内核态，此时进入了 0 特权级，因此需要用到 0 特权级栈，但系统调用的参数还在 3 特权级的栈中，为了获取用户栈地址，还得在 0 特权级栈中获取处理器自动压入的用户栈的 SS 和 esp 寄存器的值，然后再次从用户栈中获取参数。您看，光传递参数就涉及到了多次内存访问的情况，内存比寄存器要慢，而且步骤很麻烦</p></blockquote><blockquote><p>宏_syscall 和库函数 syscall 相比，syscall 实现更灵活，对用户来说任何参数个数的系统调用都统一用一种形式，用户只要记住 syscall 就可以了，而宏_syscall 的实现比较死板，针对每种参数个数的系统调用都要有单独的形式，因此支持的参数数量必然有限，而且用户要记住 7 种形式_syscall[0-6]，调用时除了输入实参外，还要输入实参的类型，确实有些麻烦，此外这个宏会引发安全漏洞（有兴趣可自行检索相关资料），故必然会被 syscall 取代。</p></blockquote><h3 id="C-2-系统调用的实现"><a href="#C-2-系统调用的实现" class="headerlink" title="C.2 系统调用的实现"></a>C.2 系统调用的实现</h3><p>这里仿照 宏_stscall来实现，下面是大体步骤:</p><p>（1）<strong>用中断门实现系统调用，效仿 Linux 用 0x80 号中断作为系统调用的入口</strong>。<br>（2）<strong>在 IDT 中安装 0x80 号中断对应的描述符，在该描述符中注册系统调用对应的中断处理例程</strong>。<br>（3）<strong>建立系统调用子功能表 syscall_table，利用 eax 寄存器中的子功能号在该表中索引相应的处理函数</strong>。<br>（4）<strong>用宏实现用户空间系统调用接口_syscall，最大支持 3 个参数的系统调用，故只需要完成_syscall[0-3]</strong>(其中寄存器传递参数，eax 为子功能号，ebx保存第 1 个参数，ecx 保存第 2 个参数，edx 保存第 3 个参数)</p><h4 id="C2-1-增加0x80中断描述符"><a href="#C2-1-增加0x80中断描述符" class="headerlink" title="C2.1 增加0x80中断描述符"></a>C2.1 增加0x80中断描述符</h4><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略...........................</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_CNT 0x81       <span class="comment">// 目前总共支持的中断数,这里写到0x81,支持0x00~0x80</span></span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">uint32_t</span> <span class="title function_">syscall_handler</span><span class="params">(<span class="type">void</span>)</span>;      <span class="comment">//系统调用的中断函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略...........................</span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">idt_desc_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i,lastindex = IDT_DESC_CNT<span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        make_idt_desc(&amp;idt[i],IDT_DESC_ATTR_DPL0,intr_entry_table[i]);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">//单独处理系统调用，系统调用对应的中断门 dpl 为 3，否则用户在3级环境执行int指令会产生GP异常</span></span><br><span class="line">    <span class="comment">//中断处理程序为单独的 syscall_handler */ </span></span><br><span class="line">    make_idt_desc(&amp;idt[lastindex], IDT_DESC_ATTR_DPL3,syscall_handler);</span><br><span class="line">    </span><br><span class="line">    put_str(<span class="string">"idt_desc_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略...........................</span></span><br></pre></td></tr></table></figure></div><h4 id="C2-2-实现系统调用接口"><a href="#C2-2-实现系统调用接口" class="headerlink" title="C2.2 实现系统调用接口"></a>C2.2 实现系统调用接口</h4><p>在<code>lib/user</code>下创建文件<code>syscall.c</code>，来实现系统调用接口，核心是利用内联汇编传参并处罚中断，优点就是简单容易实现:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/syscall.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//无参数的系统调用</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall0(NUMBER)({ \</span></span><br><span class="line"><span class="meta">    int retval;             \</span></span><br><span class="line"><span class="meta">    asm volatile(           \</span></span><br><span class="line"><span class="meta">        <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">        : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">        : <span class="string">"a"</span> (NUMBER)      \</span></span><br><span class="line"><span class="meta">        : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );                      \</span></span><br><span class="line"><span class="meta">    retval;                 \</span></span><br><span class="line"><span class="meta">})</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//1个参数的系统调用 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall1(NUMBER, ARG1) ({ \</span></span><br><span class="line"><span class="meta">        int retval;             \</span></span><br><span class="line"><span class="meta">        asm volatile (          \</span></span><br><span class="line"><span class="meta">            <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">            : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">            : <span class="string">"a"</span> (NUMBER), <span class="string">"b"</span> (ARG1) \</span></span><br><span class="line"><span class="meta">            : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );      \</span></span><br><span class="line"><span class="meta">    retval; \</span></span><br><span class="line"><span class="meta">})</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//2个参数的系统调用 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall2(NUMBER, ARG1, ARG2) ({ \</span></span><br><span class="line"><span class="meta">        int retval;             \</span></span><br><span class="line"><span class="meta">        asm volatile (          \</span></span><br><span class="line"><span class="meta">            <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">            : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">            : <span class="string">"a"</span> (NUMBER), <span class="string">"b"</span> (ARG1), <span class="string">"c"</span> (ARG2) \</span></span><br><span class="line"><span class="meta">            : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );      \</span></span><br><span class="line"><span class="meta">    retval; \</span></span><br><span class="line"><span class="meta">})</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//3个参数的系统调用 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall3(NUMBER, ARG1, ARG2, ARG3) ({ \</span></span><br><span class="line"><span class="meta">        int retval;             \</span></span><br><span class="line"><span class="meta">        asm volatile (          \</span></span><br><span class="line"><span class="meta">            <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">            : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">            : <span class="string">"a"</span> (NUMBER), <span class="string">"b"</span> (ARG1), <span class="string">"c"</span> (ARG2), <span class="string">"d"</span> (ARG3) \</span></span><br><span class="line"><span class="meta">            : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );      \</span></span><br><span class="line"><span class="meta">    retval; \</span></span><br><span class="line"><span class="meta">})</span></span><br></pre></td></tr></table></figure></div><h4 id="C2-3-增加-0x80-号中断处理例程"><a href="#C2-3-增加-0x80-号中断处理例程" class="headerlink" title="C2.3 增加 0x80 号中断处理例程"></a>C2.3 增加 0x80 号中断处理例程</h4><p>在<code>kernel.S</code>中添加<code>syscall_ handler</code>函数：</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel.S</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 略...........</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;;;;;;;;;;;;;;;;;;;;;; 0x80 号中断 ;;;;;;;;;;;;;;;;;;;;;;</span></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">extern</span> syscall_table        <span class="comment">;声明系统调用使用的数组</span></span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line"><span class="meta">global</span> syscall_handler      <span class="comment">;声明外部可调用</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">syscall_handler:</span></span><br><span class="line">    <span class="comment">;1.保存上下文环境</span></span><br><span class="line">    <span class="keyword">push</span> <span class="number">0</span>              <span class="comment">;压入0，使栈中格式统一</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ds</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">es</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">fs</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">gs</span></span><br><span class="line">    <span class="keyword">pushad</span>              <span class="comment">;压入32位寄存器，入栈顺序为EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">push</span> <span class="number">0x80</span>           <span class="comment">;这里压入0x80也是为了保持统一的格式</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;2. 为系统调用子功能传入参数</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">edx</span>            <span class="comment">;第3个参数</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>            <span class="comment">;第2个参数</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ebx</span>            <span class="comment">;第1个参数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;3. 调用子功能处理函数</span></span><br><span class="line">    <span class="keyword">call</span> [syscall_table + <span class="built_in">eax</span>*<span class="number">4</span>]</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">12</span>              <span class="comment">;跨过上面的三个参数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;4. 将call调用后的返回值存入当前内核栈的eax的位置</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">esp</span> +<span class="number">8</span>*<span class="number">4</span>],<span class="built_in">eax</span><span class="comment">;</span></span><br><span class="line">    <span class="keyword">jmp</span> intr_exit               <span class="comment">;intr_exit返回，恢复上下文</span></span><br></pre></td></tr></table></figure></div><p>然后修改<code>thread.c/h</code>，在结构体添加pid变量/锁，并且增加初始化和获取</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.h</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略............................</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//自定义的pid类型</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">int16_t</span> <span class="type">pid_t</span>;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">pid_lock</span>;</span> <span class="comment">// 分配 pid 锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进程或线程的 pcb，程序控制块 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span>* self_kstack;      <span class="comment">// 各内核线程都用自己的内核栈,注意为首位</span></span><br><span class="line">    <span class="type">pid_t</span> pid;                  <span class="comment">// 任务的pid</span></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">task_status</span> <span class="title">status</span>;</span>    <span class="comment">// 任务状态</span></span><br><span class="line">    <span class="type">uint8_t</span> priority;           <span class="comment">// 线程优先级</span></span><br><span class="line">    <span class="type">char</span> name[<span class="number">16</span>];              <span class="comment">// 名称</span></span><br><span class="line">    <span class="type">uint8_t</span> ticks;              <span class="comment">// 每次在处理器上执行的时间滴答数,每次时钟中断都会对其减1，到0则换下处理器</span></span><br><span class="line">    <span class="type">uint32_t</span> elapsed_ticks;     <span class="comment">// 从cpu运行至今占用了多少cpu滴答数</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">general_tag</span>;</span>       <span class="comment">//用于线程在一般队列的结点  </span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">all_list_tag</span>;</span>      <span class="comment">//用于线程队列thread_all_list的结点</span></span><br><span class="line">    <span class="type">uint32_t</span>* pgdir;                    <span class="comment">//进程自己的虚拟地址     </span></span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">userprog_vaddr</span>;</span>     <span class="comment">//用户进程的虚拟地址</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> stack_magic;               <span class="comment">// 栈的边界标记，用于检测栈的溢出，注意为尾部</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//略............................</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略................</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//分配pid</span></span><br><span class="line"><span class="type">static</span> <span class="type">pid_t</span> <span class="title function_">allocate_pid</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    lock_acquire(&amp;pid_lock);</span><br><span class="line">    <span class="type">static</span> <span class="type">pid_t</span> next_pid = <span class="number">0</span>;</span><br><span class="line">    next_pid++;</span><br><span class="line">    lock_release(&amp;pid_lock);</span><br><span class="line">    <span class="keyword">return</span> next_pid;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化线程</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"thread_init start\n"</span>);</span><br><span class="line"></span><br><span class="line">    list_init(&amp;thread_ready_list);</span><br><span class="line">    list_init(&amp;thread_all_list);</span><br><span class="line">    lock_init(&amp;pid_lock);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//将当前的main函数创建为线程</span></span><br><span class="line">    make_main_thread();</span><br><span class="line">    put_str(<span class="string">"thread_init done"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="C2-4-初始化系统调用和实现-sys-getpid"><a href="#C2-4-初始化系统调用和实现-sys-getpid" class="headerlink" title="C2.4 初始化系统调用和实现 sys_getpid"></a>C2.4 初始化系统调用和实现 sys_getpid</h4><p>下面在<code>/mouse/userprog</code>下添加<code>syscall-init.c/h</code></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/syscall-init.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> syscall_nr 32 </span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span>* syscall; </span><br><span class="line">syscall syscall_table[syscall_nr];      <span class="comment">//存储系统调用的函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回当前任务的 pid */</span> </span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">sys_getpid</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="keyword">return</span> running_thread()-&gt;pid; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化系统调用 */</span> </span><br><span class="line"><span class="type">void</span> <span class="title function_">syscall_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    put_str(<span class="string">"syscall_init start\n"</span>); </span><br><span class="line">    syscall_table[SYS_GETPID] = sys_getpid; </span><br><span class="line">    put_str(<span class="string">"syscall_init done\n"</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/syscall-init.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __USERPROG_SYSCALL_INIT_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __USERPROG_SYSCALL_INIT_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">sys_getpid</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">syscall_init</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="C2-5-添加系统调用-getpid"><a href="#C2-5-添加系统调用-getpid" class="headerlink" title="C2.5 添加系统调用 getpid"></a>C2.5 添加系统调用 getpid</h4><p>现在来完善前面写的<code>syscall.c</code>,同时添加<code>syscall.h</code>，来实现我们的第一个系统调用</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/syscall.hs</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">SYSCALL_NR</span>{</span>        <span class="comment">//存放系统调用子功能号</span></span><br><span class="line">    SYS_GETPID</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">getpid</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/syscall.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//略...................</span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">getpid</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> _syscall0(SYS_GETPID);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="C2-6-在用户进程中的系统调用"><a href="#C2-6-在用户进程中的系统调用" class="headerlink" title="C2.6 在用户进程中的系统调用"></a>C2.6 在用户进程中的系统调用</h4><p>注意将之前的初始化添加到<code>init.c</code>文件中,同时更新相关子Makefile文件，这里选择在主函数中模拟验证系统调用结果，分别用户内核函数中调用获取pid的函数，然后通过内核线程输出:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">int</span> test_var_a = <span class="number">0</span>, test_var_b = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   put_str(<span class="string">"I am kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line">   process_execute(u_prog_a, <span class="string">"user_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"user_prog_b"</span>);</span><br><span class="line"></span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"argA "</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"argB "</span>);</span><br><span class="line"></span><br><span class="line">   intr_enable();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      console_put_str(<span class="string">"ua:"</span>);</span><br><span class="line">      console_put_int(test_var_a);</span><br><span class="line">      console_put_str(<span class="string">" ka:"</span>);</span><br><span class="line">      console_put_int(sys_getpid());</span><br><span class="line">      console_put_str(<span class="string">" "</span>);</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      console_put_str(<span class="string">"ub:"</span>);</span><br><span class="line">      console_put_int(test_var_b);</span><br><span class="line">      console_put_str(<span class="string">" kb:"</span>);</span><br><span class="line">      console_put_int(sys_getpid());</span><br><span class="line">      console_put_str(<span class="string">" "</span>);</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      test_var_a = getpid();</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      test_var_b = getpid();</span><br><span class="line">   }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>到这里，如果屏幕上打印出四个任务(除了主函数)的pid的话，那么就成功啦！</p><h3 id="C-3-让用户进程说话"><a href="#C-3-让用户进程说话" class="headerlink" title="C.3 让用户进程说话"></a>C.3 让用户进程说话</h3><blockquote><p>函数占用的也是静态内存，因此也得提前告诉编译器自己占用的内存大小。为了在编译时获取函数调用时所需要的内存空间（这通常是在栈中分配内存单元），编译器要求提供函数声明，声明中描述了函数参数的个数及类型，编译器用它们来计算参数所占据的栈空间。因此编译器不关心函数声明中参数的名称，它只关心参数个数及类型（您懂的，函数声明中的参数可以不包括参数名，但必须包括类型），编译器用这两个信息才能确定为函数在栈中分配的内存大小。重点来了，函数并不是在堆中分配内存，因此它需要提前确定内存空间，这通常取决于参数的个数及类型大小，但编译器却允许函数的参数个数不固定（可变参数），怎么看上去显得那么“动态”？其实可变参数的这种“动态”只是一种幻想，本质上还是静态，这一切得益于编译器采用 C 调用约定来处理函数的传参方式。C调用约定规定：<strong>由调用者把参数以从右向左的顺序压入栈中，并且由调用者清理堆栈中的参数</strong>。我们拿格式化输出函数 printf(char* format, arg1, arg2，…)举例，其中的参数 format 就是大伙儿再熟悉不过的包含“%类型字符”的字符串</p></blockquote><p>既然参数是由调用者压入的，调用者当然知道栈中压入了几个参数，参数占用了多少空间，因此无论函数的参数个数是否固定，采用 C 调用约定，调用者都能完好地回收栈空间，不必担心栈溢出等问题</p><blockquote><p>如何知道栈中有多少个参数呢，如何找到它们呢？其实答案全在格式化字符串中，此字符串通常称为 format，在格式化字符串中的字符’%’便是在栈中寻找可变参数的依据，紧跟’%’后面的是类型字符，类型字符表示数据类型和进制相关的内容。格式化字符串中有多少’%’，就在栈中找多少次参数，尽管用户（程序员）输入’%’的数量可以和参数个数不一致，但那是用户自己的事，除非用户愿意搬起石头砸自己的脚，编译器也不会检查它们的数量是否匹配，因为参数处理与否是函数自己的行为，由函数体内的代码决定。总之正常情况下，<strong>用户传入可变参数的数量应与 format 中字符’%’的数量匹配</strong>，以 format 中的’%’作为参数的线索，每找到一个’%’，就到栈中去找一次参数</p></blockquote><p>下面直接通过代码来了解什么是可变参数:</p><h4 id="C3-1-实现系统调用write"><a href="#C3-1-实现系统调用write" class="headerlink" title="C3.1 实现系统调用write"></a>C3.1 实现系统调用write</h4><p>printf 函数来完成的，它是标准 io 函数，printf 函数是“格式化”“输出”函数，但它只是个外壳，真正起到“格式化”作用的是<code>vsprintf</code>函数，真正起“输出”作用的是<code>write</code>系统调用</p><p>下面我们来实现一个简易版的write，因为标准实现还需要<code>fd</code>(文件描述符)，这个要等到后面学习文件系统的时候才能实现</p><p>首先，现在<code>syscall.h</code>中添加新的子功能号：</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/syscall.hs</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">SYSCALL_NR</span>{</span>        <span class="comment">//存放系统调用子功能号</span></span><br><span class="line">    SYS_GETPID,</span><br><span class="line">    SYS_WRITE</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">getpid</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">write</span><span class="params">(<span class="type">char</span>* str)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/syscall.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略..........</span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">write</span><span class="params">(<span class="type">char</span>* str)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> _syscall1(SYS_WRITE,str);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>同时在<code>syscall_table</code>中注册</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/syscall-init.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略..................</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化系统调用 */</span> </span><br><span class="line"><span class="type">void</span> <span class="title function_">syscall_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    put_str(<span class="string">"syscall_init start\n"</span>); </span><br><span class="line">    syscall_table[SYS_GETPID] = sys_getpid; <span class="comment">//获取pid</span></span><br><span class="line">    syscall_table[SYS_WRITE] = sys_write;   <span class="comment">//write</span></span><br><span class="line">    put_str(<span class="string">"syscall_init done\n"</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/syscall-init.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __USERPROG_SYSCALL_INIT_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __USERPROG_SYSCALL_INIT_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">sys_getpid</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">syscall_init</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>实现方法很简单，就是使用<code>console_put_str</code>输出<code>str</code>,然后通过<code>strlen(str)</code>返回长度</p><p>这里就不测试了，下面来实现printf;</p><h4 id="C3-2-实现printf"><a href="#C3-2-实现printf" class="headerlink" title="C3.2 实现printf"></a>C3.2 实现printf</h4><p><code>int printf(const char *format, ...)</code> 是我们 C 语言标准输出函数,前面说过，printf 是 vsprintf 和 write 的封装，下面我们还需要实现 vprintf 和对可变参数解析的3个宏以及转换函数<code>itoa</code>,本节的目标是使 printf 支持十六进制输出，即完成“%x”的功能</p><p>这里简单说一下我的理解:</p><blockquote><p>printf或者可变字符的原理就是，编译器会通过将参数从右向左压入栈，然后我读取第一个参数format，然后取地址再强转成char*,就得到了栈的地址，然后偏移4个单位（32位的char*是这样的，如果想要更严谨，可以选择使用一个内存对齐的宏），得到第一个参数的位置，然后通过读取字符串中的%字符，判断%后面的内容(应该读取什么类型)，来按顺序/变量大小读取刚刚获得的栈，然后再拼接到一个字符串中，就实现了”可变参数”，而参数的具体还得取决栈/缓冲区的大小，并不是真的无限可变</p></blockquote><p>下面创建文件<code>/lib/stdio.c/h</code>，具体内容大家可以阅读注释</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/stdio.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定义用于内存对齐的宏(char short int 对齐到 4，double 对齐到8）</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _INTSIZEOF(n)   ((sizeof(n) + sizeof(int) - 1) &amp; ~(sizeof(int) - 1))</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> va_start(ap, v) (ap = (va_list)&amp;v + _INTSIZEOF(v))                  <span class="comment">//将ap指向固定参数v</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> va_arg(ap, t)   (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))      <span class="comment">//将ap返回值，然后指向下一个参数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> va_end(ap)      (ap = (va_list)0)                                   <span class="comment">//清除ap</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//将整形转换为字符 </span></span><br><span class="line"><span class="comment">//待转换的数字，转换后存储的缓冲区 转换的进制数</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">itoa</span><span class="params">(<span class="type">uint32_t</span> value,<span class="type">char</span>** buf_ptr_addr,<span class="type">uint8_t</span> base)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span> m = value % base;      <span class="comment">//取模，最先掉下来的是最低位</span></span><br><span class="line">    <span class="type">uint32_t</span> i = value / base;      <span class="comment">//取整</span></span><br><span class="line">    <span class="keyword">if</span>(i)</span><br><span class="line">    {</span><br><span class="line">        itoa(i,buf_ptr_addr,base);</span><br><span class="line">    } </span><br><span class="line">    <span class="keyword">if</span>(m &lt; <span class="number">10</span>)  <span class="comment">//如果余数是0~9</span></span><br><span class="line">    {</span><br><span class="line">        *((*buf_ptr_addr)++) = m + <span class="string">'0'</span>; <span class="comment">//数字0~9转换为字符'0'~'9'</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span>    <span class="comment">//否则余数是A~F</span></span><br><span class="line">    {</span><br><span class="line">        *((*buf_ptr_addr)++) = m - <span class="number">10</span> + <span class="string">'A'</span>;    <span class="comment">//将数字A~F转换为字符A~F</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将参数ap按照格式format输出到字符串str，并返回str长度</span></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">vsprintf</span><span class="params">(<span class="type">char</span>* str,<span class="type">const</span> <span class="type">char</span>* format,va_list ap)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">char</span>* buf_ptr = str;</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span>* index_ptr = format ;</span><br><span class="line">    <span class="type">char</span> index_char = *index_ptr;</span><br><span class="line">    <span class="type">int32_t</span> arg_int;   </span><br><span class="line">    <span class="type">char</span>* arg_str; </span><br><span class="line">    <span class="keyword">while</span>(index_char)</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">if</span>(index_char != <span class="string">'%'</span>)</span><br><span class="line">        {</span><br><span class="line">            *(buf_ptr++) = index_char;      <span class="comment">//保存当前字符</span></span><br><span class="line">            index_char = *(++index_ptr);    <span class="comment">//获取下一个字符</span></span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        }</span><br><span class="line">        index_char = *(++index_ptr);        <span class="comment">//获取%后面的字符    </span></span><br><span class="line">        <span class="keyword">switch</span> (index_char)</span><br><span class="line">        {</span><br><span class="line">            <span class="keyword">case</span> <span class="string">'x'</span>:</span><br><span class="line">            {</span><br><span class="line">                arg_int = va_arg(ap,<span class="type">int</span>);</span><br><span class="line">                itoa(arg_int,&amp;buf_ptr,<span class="number">16</span>);      <span class="comment">//将数字转换为16进制并存储到buf_ptr</span></span><br><span class="line">                index_char = *(++index_ptr);    <span class="comment">//跳过格式字符并更新index_char</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">case</span> <span class="string">'c'</span>:</span><br><span class="line">            {</span><br><span class="line">                *(buf_ptr++) = va_arg(ap,<span class="type">char</span>);</span><br><span class="line">                index_char = *(++index_ptr);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">case</span> <span class="string">'s'</span>:</span><br><span class="line">            {</span><br><span class="line">                arg_str = va_arg(ap,<span class="type">char</span>*);</span><br><span class="line">                <span class="built_in">strcpy</span>(buf_ptr,arg_str);        <span class="comment">//将字符串拷贝到buf中,拷贝并不会修改指针位置</span></span><br><span class="line">                buf_ptr += <span class="built_in">strlen</span>(arg_str);     <span class="comment">//buf移动到下个需要写入的地址</span></span><br><span class="line">                index_char = *(++index_ptr);    <span class="comment">//跳过格式字符并更新index_char</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">case</span> <span class="string">'d'</span>:</span><br><span class="line">            {</span><br><span class="line">                arg_int = va_arg(ap,<span class="type">int</span>);</span><br><span class="line">                <span class="comment">//若为负数，转为正数之后，在正数之前添加一个符号'-'</span></span><br><span class="line">                <span class="keyword">if</span>(arg_int &lt; <span class="number">0</span>)</span><br><span class="line">                {</span><br><span class="line">                    arg_int = <span class="number">0</span> - arg_int;</span><br><span class="line">                    *buf_ptr++ = <span class="string">'-'</span>; </span><br><span class="line">                }</span><br><span class="line">                itoa(arg_int,&amp;buf_ptr,<span class="number">10</span>);</span><br><span class="line">                index_char = *(++index_ptr);    <span class="comment">//跳过格式字符并更新index_char</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">strlen</span>(buf_ptr);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//格式化输出字符format</span></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">printf</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* format,...)</span></span><br><span class="line">{</span><br><span class="line">    va_list args;   </span><br><span class="line">    va_start(args,format);      <span class="comment">//使args指向format的栈地址</span></span><br><span class="line">    <span class="type">char</span> buf[<span class="number">1024</span>] = {<span class="number">0</span>};       <span class="comment">//用于存储拼接后的字符串</span></span><br><span class="line">    <span class="built_in">vsprintf</span>(buf,format,args);  <span class="comment">//转换可变参</span></span><br><span class="line">    va_end(args);               <span class="comment">//清理原指针</span></span><br><span class="line">    <span class="keyword">return</span> write(buf);          <span class="comment">//通过write输出到终端</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">sprintf</span><span class="params">(<span class="type">char</span>* buf, <span class="type">const</span> <span class="type">char</span>* format, ...)</span></span><br><span class="line">{</span><br><span class="line">    va_list args;   </span><br><span class="line">    <span class="type">uint32_t</span> retval;                        <span class="comment">//保存返回值</span></span><br><span class="line">    va_start(args,format);                  <span class="comment">//使args指向format的栈地址</span></span><br><span class="line">    retval = <span class="built_in">vsprintf</span>(buf,format,args);     <span class="comment">//转换可变参到buf中</span></span><br><span class="line">    va_end(args);                           <span class="comment">//清理原指针</span></span><br><span class="line">    <span class="keyword">return</span> retval;                          <span class="comment">//返回字符串长度</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/stdio.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_STDIO_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_STDIO_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">char</span>* va_list;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">printf</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str,...)</span>;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">vsprintf</span><span class="params">(<span class="type">char</span>* str,<span class="type">const</span> <span class="type">char</span>* format,va_list ap)</span>;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">sprintf</span><span class="params">(<span class="type">char</span>* buf, <span class="type">const</span> <span class="type">char</span>* format, ...)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>最后我们在主函数中测试:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">int</span> test_var_a = <span class="number">0</span>, test_var_b = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   put_str(<span class="string">"I am kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line">   <span class="comment">// process_execute(u_prog_a, "user_prog_a");</span></span><br><span class="line">   <span class="comment">// process_execute(u_prog_b, "user_prog_b");</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">// thread_start("k_thread_a", 31, k_thread_a, "argA ");</span></span><br><span class="line">   <span class="comment">// thread_start("k_thread_b", 31, k_thread_b, "argB ");</span></span><br><span class="line">   <span class="type">int</span> a = <span class="number">-2</span>;</span><br><span class="line">   <span class="type">char</span> b = <span class="string">'c'</span>;</span><br><span class="line">   <span class="type">char</span>* eee = <span class="string">"mouse"</span>;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"Hello,Word %x %d %c %s\n"</span>,getpid(),a,b,eee);</span><br><span class="line">   intr_enable();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      console_put_str(<span class="string">"ua:"</span>);</span><br><span class="line">      console_put_int(test_var_a);</span><br><span class="line">      console_put_str(<span class="string">" ka:"</span>);</span><br><span class="line">      console_put_int(sys_getpid());</span><br><span class="line">      console_put_str(<span class="string">" "</span>);</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      console_put_str(<span class="string">"ub:"</span>);</span><br><span class="line">      console_put_int(test_var_b);</span><br><span class="line">      console_put_str(<span class="string">" kb:"</span>);</span><br><span class="line">      console_put_int(sys_getpid());</span><br><span class="line">      console_put_str(<span class="string">" "</span>);</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      test_var_a = getpid();</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      test_var_b = getpid();</span><br><span class="line">   }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>注意别忘了在Makefile中添加新增的文件，然后<code>make mouse</code>,运行之后就可以看到屏幕上正常显示便是成功啦，但注意我们现在还不支持浮点数</p><hr><h3 id="C-4-完善堆内存管理"><a href="#C-4-完善堆内存管理" class="headerlink" title="C.4 完善堆内存管理"></a>C.4 完善堆内存管理</h3><p>完善堆内存管理也就是来实现malloc的接口以及底层实现</p><h4 id="C4-1-malloc-底层原理"><a href="#C4-1-malloc-底层原理" class="headerlink" title="C4.1 malloc 底层原理"></a>C4.1 malloc 底层原理</h4><p>这里要引用一个新的名词：“arena”，这里还是引用书中部分句子</p><blockquote><p>arena 是由“一大块内存”被划分成无数“小内存块”的内存仓库，我们在原有内存管理系统的基础上实现 arena，大伙儿知道，原有系统只能分配 4KB 粒度的内存页框，因此 arena 的这“一大块内存”也是通过 malloc_page 获得的以 4KB 为粒度的内存，根据请求的内存量的大小，arena 的大小也许是 1 个页框，也许是多个页框，随后再将它们平均拆分成多个小内存块。按内存块的大小，可以划分出多种不同规格的 arena，比如一种 arena 中全是 16 字节大小的内存块，故它只响应 16 字节以内的内存分配，另一种arena 中全是 32 字节的内存块，故它只响应 32 字节以内的内存分配。我们平时调用 malloc 申请内存时，操作系统返回的地址其实就是某个内存块的起始地址，操作系统会根据 malloc 申请的内存大小来选择不同规格的内存块。因此，为支持多种容量内存块的分配，我们要提前建立好多种不同容量内存块的 arena</p></blockquote><blockquote><p>arena 是个提供内存分配的数据结构，它分为两部分，一部分是元信息，用来描述自己内存池中空闲内存块数量，这其中包括内存块描述符指针（后面介绍），通过它可以间接获知本 arena 所包含内存块的规格大小，此部分占用的空间是固定的，约为 12 字节。另一部分就是内存池区域，这里面有无数的内存块，此部分占用 arena 大量的空间。我们把每个内存块命名为 mem_block，它们是内存分配粒度更细的资源，最终为用户分配的就是这其中的一个内存块。在咱们的实现中，针对小内存块的 arena 占用 1 页框内存，除了元信息外的剩下的内存被平均分成多个小内存块</p></blockquote><blockquote><p>尽管 arena 用小内存块来满足小内存量的分配，但实际上，arena 为内存分配提供了统一的入口，无论申请的内存量是多大，都可以用同一个 arena 来分配内存。小内存块的容量虽然有几种规格，但毕竟是为满足“小”内存量分配的，最大内存块容量不会超过 1024 字节，如果申请的内存量较大，超过 1024 字节，单独的一个小内存块无法满足需求时，这时候您可能想，将多个内存块组合到一起，肯定能满足需求，团结力量大嘛。方法虽具有可行性，但还是太麻烦了，动态维护内存块的信息会增加编程复杂性，这似乎有些像 Linux 的 buddy 系统啦。其实咱们的应用很简单，根本用不着那么麻烦，处理大内存请求时也会创建个 arena，但不会再将它拆分成小内存块，而是直接将整块大内存分配出去，确实有些简单粗暴，但很有效。故此类 arena 没有对应的内存块描述符，元信息中的内存块描述符指针为空</p></blockquote><blockquote><p>当申请的内存大于 1024 字节时，因此我们对大内存的定义就是大于1024 字节。为什么要以 1024 为界限呢？有这样一个提前，就是用于处理小内存块时，我们为 arena 分配 1 页框也就是 4KB 大小的内存，我们已经介绍过了，每个 arena 都分为两部分，一部分是占用空间很少的元信息，除元信息外的剩余部分才用于内存块的划分，因此，真正用于内存块的部分不足 4KB。内存块是平均划分的，所以最大的内存块肯定要小于 2KB，这里我们以 2 为底的指数方程来划分内存块，因此最大的内存块是1024 字节，也就是说对内存块规格为 1024 字节的 arena 来说，它只有 3 个内存块，每个都是 1024 字节，剩余的部分就浪费了。咱们这里的内存块以 16 字节为起始，<strong>向上依次是 32 字节、64 字节、128 字节、256字节、512 字节、1024 字节，总共 7 种规格</strong>，因此，内存块描述符也就这 7 种。再次强调一下，<strong>每种 arena中只有一种规格的内存块，并不是同时包含多种规格，比如要么该 arena 中全是 16 字节大小的内存块，要么全是 512 字节的内存块</strong>。对于小内存块来说，系统为 arena 分配的内存总共为 4KB，因此，不同规格arena 中的内存块数量也是不同的，举例来说，假设 arena 元信息大小为 12 字节，对于内存块规格 16 字节的 arena，其包括的内存块数量是(4096-12)/16，对于内存块规格 128 字节的 arena，其包括的内存块数量是(4096-12)/128</p></blockquote><hr><p>下面还是在代码中理解:</p><h4 id="C4-2-底层初始化"><a href="#C4-2-底层初始化" class="headerlink" title="C4.2 底层初始化"></a>C4.2 底层初始化</h4><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*略................*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存块 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span>{</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">free_elem</span>;</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存块描述符 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span>{</span></span><br><span class="line">    <span class="type">uint32_t</span> block_size;        <span class="comment">//内存块大小</span></span><br><span class="line">    <span class="type">uint32_t</span> blocks_per_arena;   <span class="comment">//本arena可容纳此mem_block的数量</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">free_list</span>;</span>      <span class="comment">//目前可用的mem_block链表</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 16、32、64、128、256、512、1024 字节</span></span><br><span class="line"><span class="comment">// 当申请的内存大小超过 1024 时就直接返回一个页框，不再从 arena 中划分</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DESC_CNT 7             <span class="comment">//内存块描述符个数</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*略................*/</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*略................*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//内存仓库</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">arena</span>{</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span>* <span class="title">desc</span>;</span>        <span class="comment">//此arena关联的mem_block_desc 描述符</span></span><br><span class="line">    <span class="comment">//large为true时，cnt表示的是叶框数</span></span><br><span class="line">    <span class="comment">//否则表示空闲mem_block的数量</span></span><br><span class="line">    <span class="type">uint32_t</span> cnt;</span><br><span class="line">    <span class="type">bool</span> large;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span> <span class="title">k_block_descs</span>[<span class="title">DESC_CNT</span>];</span>  <span class="comment">//内核内存块描述符数组</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> <span class="title">kernel_pool</span>, <span class="title">user_pool</span>;</span>      <span class="comment">// 生成内核内存池和用户内存池</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">kernel_vaddr</span>;</span> <span class="comment">// 此结构是用来给内核分配虚拟地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*略................*/</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//为malloc做准备 -- 注意在头文件声明，等会其它文件中需要调用</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">block_desc_init</span><span class="params">(<span class="keyword">struct</span> mem_block_desc* desc_array)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint16_t</span> desc_idx,block_size = <span class="number">16</span>;</span><br><span class="line">    <span class="comment">//初始化每个mem_block_desc结构</span></span><br><span class="line">    <span class="keyword">for</span>(desc_idx = <span class="number">0</span>;desc_idx &lt; DESC_CNT;desc_idx++)</span><br><span class="line">    {</span><br><span class="line">        desc_array[desc_idx].block_size = block_size;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//初始化arena中的内存块数量</span></span><br><span class="line">        desc_array[desc_idx].blocks_per_arena = (PG_SIZE - <span class="keyword">sizeof</span>(<span class="keyword">struct</span> arean))/block_size;</span><br><span class="line">        list_init(&amp;desc_array[desc_idx].free_list);</span><br><span class="line">        block_size *=<span class="number">2</span>;     <span class="comment">//更新为下一个内存块规格</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存管理部分初始化入口 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mem_init</span><span class="params">()</span> </span><br><span class="line">{</span><br><span class="line">   put_str(<span class="string">"mem_init start\n"</span>);</span><br><span class="line">   <span class="type">uint32_t</span> mem_bytes_total = (*(<span class="type">uint32_t</span>*)(<span class="number">0xb00</span>));</span><br><span class="line">   lock_init(&amp;kernel_pool.lock);    <span class="comment">//初始化锁</span></span><br><span class="line">   lock_init(&amp;user_pool.lock);</span><br><span class="line">   mem_pool_init(mem_bytes_total);    <span class="comment">//初始化内存池</span></span><br><span class="line">   block_desc_init(k_block_descs);      <span class="comment">//初始化mem_block_desc数组</span></span><br><span class="line">   put_str(<span class="string">"mem_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><hr><h4 id="C4-3-实现sts-malloc"><a href="#C4-3-实现sts-malloc" class="headerlink" title="C4.3 实现sts_malloc"></a>C4.3 实现sts_malloc</h4><p>内存资源需要在需要的时候由系统动态创建，创建它的函数就是<code>sys_malloc</code>,下面要在<code>thread.h</code>中对pcb略微改动一下</p><p>为了实现用户进程的堆内存管理，在 pcb 中增加了内存块描述符数组 u_block_desc[DESC_CNT],同理，它也要初始化，所以要在<code>process.c</code>中的<code>process_execute</code>中进行初始化工作</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*略................*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进程或线程的 pcb，程序控制块 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span>* self_kstack;      <span class="comment">// 各内核线程都用自己的内核栈,注意为首位</span></span><br><span class="line">    <span class="type">pid_t</span> pid;                  <span class="comment">// 任务的pid</span></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">task_status</span> <span class="title">status</span>;</span>    <span class="comment">// 任务状态</span></span><br><span class="line">    <span class="type">uint8_t</span> priority;           <span class="comment">// 线程优先级</span></span><br><span class="line">    <span class="type">char</span> name[<span class="number">16</span>];              <span class="comment">// 名称</span></span><br><span class="line">    <span class="type">uint8_t</span> ticks;              <span class="comment">// 每次在处理器上执行的时间滴答数,每次时钟中断都会对其减1，到0则换下处理器</span></span><br><span class="line">    <span class="type">uint32_t</span> elapsed_ticks;     <span class="comment">// 从cpu运行至今占用了多少cpu滴答数</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">general_tag</span>;</span>       <span class="comment">//用于线程在一般队列的结点  </span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">all_list_tag</span>;</span>      <span class="comment">//用于线程队列thread_all_list的结点</span></span><br><span class="line">    <span class="type">uint32_t</span>* pgdir;                    <span class="comment">//进程自己的虚拟地址     </span></span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">userprog_vaddr</span>;</span>                 <span class="comment">//用户进程的虚拟地址</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span> <span class="title">u_block_desc</span>[<span class="title">DESC_CNT</span>];</span>       <span class="comment">//用户进程内存块描述符</span></span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> stack_magic;               <span class="comment">// 栈的边界标记，用于检测栈的溢出，注意为尾部</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/*略................*/</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*略................*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//创建用户进程</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">process_execute</span><span class="params">(<span class="type">void</span>* filename,<span class="type">char</span>*name)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//pcb内核的数据结构,由内核来维护进程信息,因此要在内核内存池中申请</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">thread</span> =</span> get_kernel_pages(<span class="number">1</span>);</span><br><span class="line">    init_thread(thread,name,default_prio);  <span class="comment">//默认优先级31</span></span><br><span class="line">    create_user_vaddr_bitmap(thread);               <span class="comment">//创建位图</span></span><br><span class="line">    thread_create(thread,start_process,filename);   <span class="comment">//通过start_process函数退出中断进入用户进程</span></span><br><span class="line">    thread-&gt;pgdir = create_page_dir();              <span class="comment">//创建页目录表</span></span><br><span class="line">    block_desc_init(thread-&gt;u_block_desc);          <span class="comment">//初始化内存块描述符</span></span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable(); </span><br><span class="line"></span><br><span class="line">    ASSERT(!elem_find(&amp;thread_ready_list, &amp;thread-&gt;general_tag));</span><br><span class="line">    list_append(&amp;thread_ready_list, &amp;thread-&gt;general_tag);</span><br><span class="line">    ASSERT(!elem_find(&amp;thread_all_list, &amp;thread-&gt;all_list_tag));</span><br><span class="line">    list_append(&amp;thread_all_list, &amp;thread-&gt;all_list_tag);</span><br><span class="line"></span><br><span class="line">    intr_set_status(old_status);</span><br><span class="line">}</span><br><span class="line"><span class="comment">/*略................*/</span></span><br></pre></td></tr></table></figure></div><p>然后是对内存管理方面的修改<code>memory.c</code></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//返回arena中第idx个内存块的地址</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">struct</span> mem_block* <span class="title function_">arena2block</span><span class="params">(<span class="keyword">struct</span> arena* a,<span class="type">uint32_t</span> idx)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">struct</span> mem_block*) ((<span class="type">uint32_t</span>)a + <span class="keyword">sizeof</span>(<span class="keyword">struct</span> arena) + idx * a-&gt;desc-&gt;block_size);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//返回内存块b所在的arena地址</span></span><br><span class="line"><span class="comment">//由于此类内存块所在的 arena 占据 1 个完整的自然页框，所以 arena 中的内存块都属于这</span></span><br><span class="line"><span class="comment">//页框之内，因此函数原理很简单，内存块的高 20 位地址便是 arena 所在的地址</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">struct</span> arena* <span class="title function_">block2arena</span><span class="params">(<span class="keyword">struct</span> mem_block* b)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">struct</span> arena*)((<span class="type">uint32_t</span>)b &amp; <span class="number">0xfffff000</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//在堆中申请size字节内存</span></span><br><span class="line"><span class="type">void</span>* <span class="title function_">sys_malloc</span><span class="params">(<span class="type">uint32_t</span> size)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">pool_flags</span> <span class="title">PF</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span>;</span>  <span class="comment">//内存池结构</span></span><br><span class="line">    <span class="type">uint32_t</span> pool_size; </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span>* <span class="title">descs</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur_thread</span> =</span> running_thread();  <span class="comment">//获取当前pcb</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//判断是哪个内存池</span></span><br><span class="line">    <span class="keyword">if</span>(cur_thread-&gt;pgdir == <span class="literal">NULL</span>)       <span class="comment">//若为内核线程</span></span><br><span class="line">    {</span><br><span class="line">        PF = PF_KERNEL;     <span class="comment">//内核线程</span></span><br><span class="line">        pool_size = kernel_pool.pool_size;</span><br><span class="line">        mem_pool = &amp;kernel_pool;</span><br><span class="line">        descs = k_block_descs;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span>        <span class="comment">//若为用户进程pcb，pgdir不为空</span></span><br><span class="line">    {</span><br><span class="line">        PF = PF_USER;</span><br><span class="line">        pool_size = user_pool.pool_size;    </span><br><span class="line">        mem_pool = &amp;user_pool;</span><br><span class="line">        descs = cur_thread-&gt;u_block_desc;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">//若申请的内存不在内存池的容量范围内，则直接返回NULL</span></span><br><span class="line">    <span class="keyword">if</span>(!(size &gt; <span class="number">0</span> &amp;&amp; size &lt; pool_size))</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">arena</span>* <span class="title">a</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span>* <span class="title">b</span>;</span></span><br><span class="line">    lock_acquire(&amp;mem_pool-&gt;lock);      <span class="comment">//上锁</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(size&gt;<span class="number">1024</span>)   <span class="comment">//如果大于1024，就直接分配页框</span></span><br><span class="line">    {</span><br><span class="line">        <span class="type">uint32_t</span> page_cnt = DIV_ROUND_UP(size + <span class="keyword">sizeof</span>(<span class="keyword">struct</span> arena),PG_SIZE);  <span class="comment">//直接向上取整</span></span><br><span class="line"></span><br><span class="line">        a = malloc_page(PF,page_cnt);       <span class="comment">//获取页框</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(a!= <span class="literal">NULL</span>)</span><br><span class="line">        {</span><br><span class="line">            <span class="built_in">memset</span>(a,<span class="number">0</span>,page_cnt * PG_SIZE); <span class="comment">//将分配的内存清0</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">//对于分配的大块页框，将desc置NULL，cnt就是页框数，large置true</span></span><br><span class="line">            a-&gt;desc = <span class="literal">NULL</span>;</span><br><span class="line">            a-&gt;cnt = page_cnt;</span><br><span class="line">            a-&gt;large = <span class="literal">true</span>;</span><br><span class="line">            lock_release(&amp;mem_pool-&gt;lock);  <span class="comment">//解锁</span></span><br><span class="line">            <span class="keyword">return</span> (<span class="type">void</span>*)(a+<span class="number">1</span>);            <span class="comment">//跨过arena大小，把剩下的内存返回</span></span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        {</span><br><span class="line">            lock_release(&amp;mem_pool-&gt;lock);  <span class="comment">//解锁</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span>            <span class="comment">//小于1024，则通过不同的mem_block_desc去适配</span></span><br><span class="line">    {</span><br><span class="line">        <span class="type">uint8_t</span> desc_idx;</span><br><span class="line">        <span class="comment">//从内存块描述符中找到合适的内存块规格</span></span><br><span class="line">        <span class="keyword">for</span>(desc_idx = <span class="number">0</span>; desc_idx &lt; DESC_CNT ; desc_idx++)</span><br><span class="line">        {</span><br><span class="line">            <span class="keyword">if</span>(size &lt;= descs[desc_idx].block_size)</span><br><span class="line">            {<span class="comment">//从小到大寻找</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">//若 mem_block_desc 的 free_list 中已经没有可用的 mem_block，就创建新的 arena 提供 mem_block</span></span><br><span class="line">        <span class="keyword">if</span>(list_empty(&amp;descs[desc_idx].free_list))</span><br><span class="line">        {</span><br><span class="line">            a = malloc_page(PF,<span class="number">1</span>);      <span class="comment">//分配1页框作为arena</span></span><br><span class="line">            <span class="keyword">if</span>(a == <span class="literal">NULL</span>)</span><br><span class="line">            {</span><br><span class="line">                lock_release(&amp;mem_pool-&gt;lock);</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">            }</span><br><span class="line">            <span class="built_in">memset</span>(a,<span class="number">0</span>,PG_SIZE);        <span class="comment">//将内存块清零</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">//对于分配的小块内存，将desc置为相应的内存块描述符</span></span><br><span class="line">            <span class="comment">//cnt为此arena可用的内存块数，large置为flase</span></span><br><span class="line">            a-&gt;desc = &amp;descs[desc_idx];</span><br><span class="line">            a-&gt;large = <span class="literal">false</span>;</span><br><span class="line">            a-&gt;cnt = descs[desc_idx].blocks_per_arena;</span><br><span class="line">            <span class="type">uint32_t</span> block_idx;</span><br><span class="line"></span><br><span class="line">            <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();  <span class="comment">//关中断  </span></span><br><span class="line">            <span class="comment">//开始将arena拆分为内存块，并添加到内存块描述符的free_list中</span></span><br><span class="line">            <span class="keyword">for</span>(block_idx = <span class="number">0</span>;block_idx &lt; descs[desc_idx].blocks_per_arena;block_idx++)</span><br><span class="line">            {</span><br><span class="line">                b = arena2block(a,block_idx);   <span class="comment">//  寻找第block_idx个内存块初始地址</span></span><br><span class="line">                ASSERT(!elem_find(&amp;a-&gt;desc-&gt;free_list, &amp;b-&gt;free_elem)); <span class="comment">//保证free_list中没有</span></span><br><span class="line">                list_append(&amp;a-&gt;desc-&gt;free_list,&amp;b-&gt;free_elem);         <span class="comment">//添加到链表中</span></span><br><span class="line">            }</span><br><span class="line">            intr_set_status(old_status);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">//开始分配内存块</span></span><br><span class="line">        b = elem2entry(<span class="keyword">struct</span> mem_block,free_elem, list_pop(&amp;(descs[desc_idx].free_list))); <span class="comment">//通过elem找到mem_block的地址，即之前内存块拆分的时候b的地址，这就是最终分配的地址</span></span><br><span class="line">        <span class="built_in">memset</span>(b,<span class="number">0</span>,descs[desc_idx].block_size);     <span class="comment">//置0</span></span><br><span class="line">        a = block2arena(b);                         <span class="comment">//获取b所在的arena;</span></span><br><span class="line">        a-&gt;cnt--;                                   <span class="comment">//此arena中的空闲内存块数减1</span></span><br><span class="line">        lock_release(&amp;mem_pool-&gt;lock);              <span class="comment">//关锁</span></span><br><span class="line">        <span class="keyword">return</span> (<span class="type">void</span>*)b;                            </span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后我们在头文件添加对应的声明，最后就是对这个函数进行测试了</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">int</span> test_var_a = <span class="number">0</span>, test_var_b = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   put_str(<span class="string">"I am kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"argA "</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"argB "</span>);</span><br><span class="line"></span><br><span class="line">   intr_enable();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="type">void</span>* addr = sys_malloc(<span class="number">33</span>); </span><br><span class="line">   console_put_str(<span class="string">" I am thread_a, sys_malloc(33), addr is 0x"</span>); </span><br><span class="line">   console_put_int((<span class="type">int</span>)addr); </span><br><span class="line">   console_put_char(<span class="string">'\n'</span>); </span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="type">void</span>* addr = sys_malloc(<span class="number">63</span>); </span><br><span class="line">   console_put_str(<span class="string">" I am thread_b, sys_malloc(63), addr is 0x"</span>); </span><br><span class="line">   console_put_int((<span class="type">int</span>)addr); </span><br><span class="line">   console_put_char(<span class="string">'\n'</span>); </span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      test_var_a = getpid();</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) {</span><br><span class="line">      test_var_b = getpid();</span><br><span class="line">   }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在两个内核线程中分别申请33,63字节的内存，按照我们写的逻辑，都会选择分配64字节的内存，因此 sys_malloc 会创建规格为 64 字节的 arena，然后把它拆分成 64 字节的内存块，由于是第 1 次申请内存且只申请了一种内存块，故系统中只存在这一个 arena，那么我们的结果如果相差16进制的4，也就是十进制的64字节，说明它给分配的就是64字节，符合预期</p><blockquote><p>I am thread_b, sys_malloc(63), addr is OxC010204C<br>I am thread_a, sys_malloc(33), addr is OxC010200C</p></blockquote><hr><h4 id="C4-4-内存的释放"><a href="#C4-4-内存的释放" class="headerlink" title="C4.4 内存的释放"></a>C4.4 内存的释放</h4><p>内存管理系统不仅能分配内存，还应该能回收内存，这是最基本的内存管理机制</p><blockquote><p>回忆一下：内存的使用情况都是通过位图来管理的，因此，无论内存的分配或释放，本质上都是在设置相关位图中的相应位，都是在读写位图。回收物理地址就是将物理内存池位图中的相应位清 0，无需将该 4KB 物理页框逐字节清 0。回收虚拟地址就是将虚拟内存池位图中的相应位清 0。分配则是相反的，也就是将位图中相应位置为 1 即可</p></blockquote><p>在之前，我们申请的内存是通过<code>malloc_page</code>中，分别经历<strong>在虚拟地址池和物理地址池中分配地址（位图同理）</strong>，在<strong>页表中完成虚拟地址和物理地址的映射</strong></p><p>而释放内存刚好和这个相反，我们首先要<strong>在物理地址池释放物理页地址(位图同理)</strong>，<strong>在页表中去掉虚拟地址的映射</strong>(将虚拟地址对应 pte 的 P 位置 0 <code>page_table_ pte_remove</code>)，最后<strong>在虚拟地址池中释放虚拟地址(vaddr_remove)</strong><br>下面我们将这几个步骤封装到函数<code>mfree_page中</code></p><p>开始搓代码:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.c</span></span><br><span class="line"><span class="comment">//略............</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//将物理地址pg_phy_addr 回收到物理内存池-- 回收一个物理页</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">pfree</span><span class="params">(<span class="type">uint32_t</span> pg_phy_addr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span>;</span></span><br><span class="line">    <span class="type">uint32_t</span> bit_idx = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span>(pg_phy_addr &gt;= user_pool.phy_addr_start)        <span class="comment">//用户物理内存池</span></span><br><span class="line">    {</span><br><span class="line">        mem_pool = &amp;user_pool;</span><br><span class="line">        bit_idx = (pg_phy_addr - user_pool.phy_addr_start)/PG_SIZE;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span>    <span class="comment">//内核物理内存池</span></span><br><span class="line">    {</span><br><span class="line">        mem_pool = &amp;kernel_pool;</span><br><span class="line">        bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start)/PG_SIZE;</span><br><span class="line">    }</span><br><span class="line">    bitmap_set(&amp;mem_pool-&gt;pool_bitmap,bit_idx,<span class="number">0</span>);       <span class="comment">//将位图清零</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//去掉页表中虚拟地址 vaddr 的映射，只去掉 vaddr 对应的 pte</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">page_table_pte_remove</span><span class="params">(<span class="type">uint32_t</span> vaddr)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">uint32_t</span>* pte = pte_ptr(vaddr); </span><br><span class="line">    *pte &amp;= ~PG_P_1;                <span class="comment">// 将页表项 pte 的 P 位置 0 </span></span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"invlpg %0"</span>::<span class="string">"m"</span> (vaddr):<span class="string">"memory"</span>)</span>; <span class="comment">//更新 tlb 快表</span></span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">//在虚拟地址池中释放以_vaddr 起始的连续 pg_cnt 个虚拟页地址</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">vaddr_remove</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="type">void</span>* _vaddr, <span class="type">uint32_t</span> pg_cnt)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">uint32_t</span> bit_idx_start = <span class="number">0</span>, vaddr = (<span class="type">uint32_t</span>)_vaddr, cnt = <span class="number">0</span>; </span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (pf == PF_KERNEL) <span class="comment">// 内核虚拟内存池</span></span><br><span class="line">    { </span><br><span class="line">        bit_idx_start = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE; </span><br><span class="line">        <span class="keyword">while</span>(cnt &lt; pg_cnt) </span><br><span class="line">        { </span><br><span class="line">            bitmap_set(&amp;kernel_vaddr.vaddr_bitmap,bit_idx_start + cnt++, <span class="number">0</span>); </span><br><span class="line">        } </span><br><span class="line">    } </span><br><span class="line">    <span class="keyword">else</span>                <span class="comment">// 用户虚拟内存池</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">struct</span> task_struct* cur_thread = running_thread(); </span><br><span class="line">        bit_idx_start =(vaddr - cur_thread-&gt;userprog_vaddr.vaddr_start) / PG_SIZE; </span><br><span class="line">        <span class="keyword">while</span>(cnt &lt; pg_cnt) </span><br><span class="line">        { </span><br><span class="line">            bitmap_set(&amp;cur_thread-&gt;userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, <span class="number">0</span>); </span><br><span class="line">        } </span><br><span class="line">    } </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//释放以虚拟地址 _vaddr 为起始的 pg_cnt 个物理页框</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mfree_page</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="type">void</span>* _vaddr, <span class="type">uint32_t</span> pg_cnt)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span> pg_phy_addr;</span><br><span class="line">    <span class="type">uint32_t</span> vaddr = (<span class="type">int32_t</span>)_vaddr,page_cnt = <span class="number">0</span>;</span><br><span class="line">    ASSERT(pg_cnt&gt;=<span class="number">1</span> &amp;&amp; vaddr%PG_SIZE == <span class="number">0</span>);</span><br><span class="line">    pg_phy_addr = addr_v2p(vaddr);  <span class="comment">//获取虚拟地址vaddr对应的物理地址</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//确保待释放的物理内存在低端1MB+1kb大小的页目录——1kb大小的页表地址范围外</span></span><br><span class="line">    ASSERT((pg_phy_addr % PG_SIZE) == <span class="number">0</span> &amp;&amp; pg_phy_addr &gt;= <span class="number">0x102000</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//判断 pg_phy_addr 属于用户物理内存池还是内核物理内存池</span></span><br><span class="line">    <span class="keyword">if</span>(pg_phy_addr &gt;= user_pool.phy_addr_start)     <span class="comment">//user</span></span><br><span class="line">    {</span><br><span class="line">        vaddr -= PG_SIZE;</span><br><span class="line">        <span class="keyword">while</span> (page_cnt &lt; pg_cnt)</span><br><span class="line">        {</span><br><span class="line">            vaddr += PG_SIZE;</span><br><span class="line">            pg_phy_addr = addr_v2p(vaddr);</span><br><span class="line">            <span class="comment">//确保物理地址属于用户物理内存池 </span></span><br><span class="line">            ASSERT((pg_phy_addr % PG_SIZE) == <span class="number">0</span> &amp;&amp; pg_phy_addr &gt;= user_pool.phy_addr_start); </span><br><span class="line">            <span class="comment">//先将对应的物理页框归还到内存池 </span></span><br><span class="line">            pfree(pg_phy_addr); </span><br><span class="line">            <span class="comment">//再从页表中清除此虚拟地址所在的页表项 pte </span></span><br><span class="line">            page_table_pte_remove(vaddr); </span><br><span class="line">            page_cnt++;</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">//清空虚拟地址的位图中的相应位</span></span><br><span class="line">        vaddr_remove(pf, _vaddr, pg_cnt);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span>            <span class="comment">//kernel</span></span><br><span class="line">    {</span><br><span class="line">        vaddr -= PG_SIZE; </span><br><span class="line">        <span class="keyword">while</span> (page_cnt &lt; pg_cnt) </span><br><span class="line">        { </span><br><span class="line">            vaddr += PG_SIZE; </span><br><span class="line">            pg_phy_addr = addr_v2p(vaddr); </span><br><span class="line">            <span class="comment">//确保待释放的物理内存只属于内核物理内存池</span></span><br><span class="line">            ASSERT((pg_phy_addr % PG_SIZE) == <span class="number">0</span> &amp;&amp; pg_phy_addr &gt;= kernel_pool.phy_addr_start &amp;&amp; pg_phy_addr &lt; user_pool.phy_addr_start); </span><br><span class="line">            <span class="comment">//先将对应的物理页框归还到内存池</span></span><br><span class="line">            pfree(pg_phy_addr); </span><br><span class="line">            <span class="comment">//再从页表中清除此虚拟地址所在的页表项 pte</span></span><br><span class="line">            page_table_pte_remove(vaddr); </span><br><span class="line">            page_cnt++; </span><br><span class="line">        } </span><br><span class="line">        <span class="comment">//清空虚拟地址的位图中的相应位</span></span><br><span class="line">        vaddr_remove(pf, _vaddr, pg_cnt);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略............</span></span><br></pre></td></tr></table></figure></div><hr><h4 id="C4-5-实现sys-free"><a href="#C4-5-实现sys-free" class="headerlink" title="C4.5 实现sys_free"></a>C4.5 实现sys_free</h4><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 回收内存 ptr</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sys_free</span><span class="params">(<span class="type">void</span>* ptr)</span> </span><br><span class="line">{ </span><br><span class="line">    ASSERT(ptr != <span class="literal">NULL</span>); </span><br><span class="line">    <span class="keyword">if</span> (ptr != <span class="literal">NULL</span>) </span><br><span class="line">    { </span><br><span class="line">        <span class="class"><span class="keyword">enum</span> <span class="title">pool_flags</span> <span class="title">PF</span>;</span> </span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span>;</span> </span><br><span class="line"></span><br><span class="line">        <span class="comment">//判断是线程，还是进程 </span></span><br><span class="line">        <span class="keyword">if</span> (running_thread()-&gt;pgdir == <span class="literal">NULL</span>)    <span class="comment">//内核</span></span><br><span class="line">        { </span><br><span class="line">            ASSERT((<span class="type">uint32_t</span>)ptr &gt;= K_HEAP_START); </span><br><span class="line">            PF = PF_KERNEL; </span><br><span class="line">            mem_pool = &amp;kernel_pool; </span><br><span class="line">        } </span><br><span class="line">        <span class="keyword">else</span>                    <span class="comment">//用户</span></span><br><span class="line">        { </span><br><span class="line">            PF = PF_USER; </span><br><span class="line">            mem_pool = &amp;user_pool; </span><br><span class="line">        } </span><br><span class="line"></span><br><span class="line">        lock_acquire(&amp;mem_pool-&gt;lock); <span class="comment">//上锁</span></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span>* <span class="title">b</span> =</span> ptr; </span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">arena</span>* <span class="title">a</span> =</span> block2arena(b); </span><br><span class="line">        <span class="comment">//把 mem_block 转换成 arena,获取元信息</span></span><br><span class="line">        ASSERT(a-&gt;large == <span class="number">0</span> || a-&gt;large == <span class="number">1</span>); </span><br><span class="line">        <span class="keyword">if</span> (a-&gt;desc == <span class="literal">NULL</span> &amp;&amp; a-&gt;large == <span class="literal">true</span>)    <span class="comment">// 大于 1024 的内存</span></span><br><span class="line">        { </span><br><span class="line">            mfree_page(PF, a, a-&gt;cnt); </span><br><span class="line">        } </span><br><span class="line">        <span class="keyword">else</span>  <span class="comment">// 小于等于 1024 的内存块</span></span><br><span class="line">        { </span><br><span class="line">            <span class="comment">//先将内存块回收到 free_list </span></span><br><span class="line">            list_append(&amp;a-&gt;desc-&gt;free_list, &amp;b-&gt;free_elem); </span><br><span class="line"></span><br><span class="line">            <span class="comment">//再判断此 arena 中的内存块是否都是空闲,如果是就释放 arena </span></span><br><span class="line">            <span class="keyword">if</span> (++a-&gt;cnt == a-&gt;desc-&gt;blocks_per_arena) </span><br><span class="line">            { </span><br><span class="line">                <span class="type">uint32_t</span> block_idx; </span><br><span class="line">                <span class="keyword">for</span> (block_idx = <span class="number">0</span>;block_idx &lt; a-&gt;desc-&gt;blocks_per_arena; block_idx++)</span><br><span class="line">                { </span><br><span class="line">                    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span>* <span class="title">b</span> =</span> arena2block(a, block_idx); </span><br><span class="line">                    ASSERT(elem_find(&amp;a-&gt;desc-&gt;free_list, &amp;b-&gt;free_elem)); </span><br><span class="line">                    list_remove(&amp;b-&gt;free_elem); </span><br><span class="line">                } </span><br><span class="line">                mfree_page(PF, a, <span class="number">1</span>); </span><br><span class="line">            } </span><br><span class="line">        } </span><br><span class="line">        lock_release(&amp;mem_pool-&gt;lock); <span class="comment">//释放锁</span></span><br><span class="line">    } </span><br><span class="line"> }</span><br></pre></td></tr></table></figure></div><p>下面还是在主函数中进行测试，这里在主函数的两个内核线程中进程1000次的获取和释放内存:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   put_str(<span class="string">"I am kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   intr_enable();</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">8</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">8</span>, k_thread_b, <span class="string">"I am thread_b "</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="type">void</span>* addr1;</span><br><span class="line">   <span class="type">void</span>* addr2;</span><br><span class="line">   <span class="type">void</span>* addr3;</span><br><span class="line">   <span class="type">void</span>* addr4;</span><br><span class="line">   <span class="type">void</span>* addr5;</span><br><span class="line">   <span class="type">void</span>* addr6;</span><br><span class="line">   <span class="type">void</span>* addr7;</span><br><span class="line">   console_put_str(<span class="string">" thread_a start\n"</span>);</span><br><span class="line">   <span class="type">int</span> max = <span class="number">1000</span>;</span><br><span class="line">   <span class="keyword">while</span> (max-- &gt; <span class="number">0</span>) {</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"a: %d\n"</span>,<span class="number">1000</span>-max);</span><br><span class="line">      <span class="type">int</span> size = <span class="number">128</span>;</span><br><span class="line">      addr1 = sys_malloc(size); </span><br><span class="line">      size *= <span class="number">2</span>; </span><br><span class="line">      addr2 = sys_malloc(size); </span><br><span class="line">      size *= <span class="number">2</span>; </span><br><span class="line">      addr3 = sys_malloc(size);</span><br><span class="line">      sys_free(addr1);</span><br><span class="line">      addr4 = sys_malloc(size);</span><br><span class="line">      size *= <span class="number">2</span>; size *= <span class="number">2</span>; size *= <span class="number">2</span>; size *= <span class="number">2</span>; </span><br><span class="line">      size *= <span class="number">2</span>; size *= <span class="number">2</span>; size *= <span class="number">2</span>; </span><br><span class="line">      addr5 = sys_malloc(size);</span><br><span class="line">      addr6 = sys_malloc(size);</span><br><span class="line">      sys_free(addr5);</span><br><span class="line">      size *= <span class="number">2</span>; </span><br><span class="line">      addr7 = sys_malloc(size);</span><br><span class="line">      sys_free(addr6);</span><br><span class="line">      sys_free(addr7);</span><br><span class="line">      sys_free(addr2);</span><br><span class="line">      sys_free(addr3);</span><br><span class="line">      sys_free(addr4);</span><br><span class="line">   }</span><br><span class="line">   console_put_str(<span class="string">" thread_a end\n"</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">char</span>* para = arg;</span><br><span class="line">   <span class="type">void</span>* addr1;</span><br><span class="line">   <span class="type">void</span>* addr2;</span><br><span class="line">   <span class="type">void</span>* addr3;</span><br><span class="line">   <span class="type">void</span>* addr4;</span><br><span class="line">   <span class="type">void</span>* addr5;</span><br><span class="line">   <span class="type">void</span>* addr6;</span><br><span class="line">   <span class="type">void</span>* addr7;</span><br><span class="line">   <span class="type">void</span>* addr8;</span><br><span class="line">   <span class="type">void</span>* addr9;</span><br><span class="line">   <span class="type">int</span> max = <span class="number">1000</span>;</span><br><span class="line">   console_put_str(<span class="string">" thread_b start\n"</span>);</span><br><span class="line">   <span class="keyword">while</span> (max-- &gt; <span class="number">0</span>) {</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"b: %d\n"</span>,<span class="number">1000</span>-max);</span><br><span class="line">      <span class="type">int</span> size = <span class="number">9</span>;</span><br><span class="line">      addr1 = sys_malloc(size);</span><br><span class="line">      size *= <span class="number">2</span>; </span><br><span class="line">      addr2 = sys_malloc(size);</span><br><span class="line">      size *= <span class="number">2</span>; </span><br><span class="line">      sys_free(addr2);</span><br><span class="line">      addr3 = sys_malloc(size);</span><br><span class="line">      sys_free(addr1);</span><br><span class="line">      addr4 = sys_malloc(size);</span><br><span class="line">      addr5 = sys_malloc(size);</span><br><span class="line">      addr6 = sys_malloc(size);</span><br><span class="line">      sys_free(addr5);</span><br><span class="line">      size *= <span class="number">2</span>; </span><br><span class="line">      addr7 = sys_malloc(size);</span><br><span class="line">      sys_free(addr6);</span><br><span class="line">      sys_free(addr7);</span><br><span class="line">      sys_free(addr3);</span><br><span class="line">      sys_free(addr4);</span><br><span class="line"></span><br><span class="line">      size *= <span class="number">2</span>; size *= <span class="number">2</span>; size *= <span class="number">2</span>; </span><br><span class="line">      addr1 = sys_malloc(size);</span><br><span class="line">      addr2 = sys_malloc(size);</span><br><span class="line">      addr3 = sys_malloc(size);</span><br><span class="line">      addr4 = sys_malloc(size);</span><br><span class="line">      addr5 = sys_malloc(size);</span><br><span class="line">      addr6 = sys_malloc(size);</span><br><span class="line">      addr7 = sys_malloc(size);</span><br><span class="line">      addr8 = sys_malloc(size);</span><br><span class="line">      addr9 = sys_malloc(size);</span><br><span class="line">      sys_free(addr1);</span><br><span class="line">      sys_free(addr2);</span><br><span class="line">      sys_free(addr3);</span><br><span class="line">      sys_free(addr4);</span><br><span class="line">      sys_free(addr5);</span><br><span class="line">      sys_free(addr6);</span><br><span class="line">      sys_free(addr7);</span><br><span class="line">      sys_free(addr8);</span><br><span class="line">      sys_free(addr9);</span><br><span class="line">   }</span><br><span class="line">   console_put_str(<span class="string">" thread_b end\n"</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="type">char</span>* name = <span class="string">"prog_a"</span>;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" I am %s, my pid:%d%c"</span>, name, getpid(),<span class="string">'\n'</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="type">char</span>* name = <span class="string">"prog_b"</span>;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" I am %s, my pid:%d%c"</span>, name, getpid(), <span class="string">'\n'</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>我们通过查看内存池位图来验证，在<code>k_thread_a</code>位置打断点</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nm build/kernel/kernel.bin |grep -P <span class="string">'k_thread_a'</span>    <span class="comment">#获取k_thread_a地址</span></span><br><span class="line"><span class="comment"># c000155f 是我得到的地址</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">lb 0xc000155f   <span class="comment">#打断点</span></span><br><span class="line"></span><br><span class="line">x/3 0xc009a000  <span class="comment">#查看位图</span></span><br></pre></td></tr></table></figure></div><p>我们可以在第一次进入断点查看一次位图，然后再过程中和结束后都查看一下位图，如果结束之后，位图的值一样，就算是获取释放成功啦</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;bochs:1&gt; lb 0xc000155f</span><br><span class="line">&lt;bochs:2&gt; c</span><br><span class="line">(0) Breakpoint 1, 0xc000155f <span class="keyword">in</span> ?? ()</span><br><span class="line">Next at t=18231903</span><br><span class="line">(0) [0x00000000155f] 0008:c000155f (unk. ctxt): push ebp                  ; 55</span><br><span class="line">&lt;bochs:3&gt; x/3 0xc009a000</span><br><span class="line">[bochs]:</span><br><span class="line">0xc009a000 &lt;bogus+       0&gt;:0x000000030x000000000x00000000</span><br></pre></td></tr></table></figure></div><p>这里的<code>0x00000003</code>,0x3 的二进制是 11，这表示用了 2 个页框，原因是创建两个线程时各用了 1 个页框做其 pcb，因此位图的使用情况与预期相符,等运行结束，也就是输出<code>thread_a/b end</code>的时候，应该和这个数字一样，并且中途如果暂停，位图的值应该会有不同的值</p><p>下面就来实现系统调用吧…….</p><hr><h4 id="C4-6-实现系统调用malloc-free"><a href="#C4-6-实现系统调用malloc-free" class="headerlink" title="C4.6 实现系统调用malloc && free"></a>C4.6 实现系统调用malloc &amp;&amp; free</h4><p>这里我们和书中一样，将这两个接口放到<code>syscall.h</code>文件中(Linux是放在stdlib.h中)</p><p>下面直接上代码，和之前的系统调用差不多</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/syscall.hs</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">SYSCALL_NR</span>{</span>        <span class="comment">//存放系统调用子功能号</span></span><br><span class="line">    SYS_GETPID,</span><br><span class="line">    SYS_WRITE,</span><br><span class="line">    SYS_MALLOC,</span><br><span class="line">    SYS_FREE</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">getpid</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">write</span><span class="params">(<span class="type">char</span>* str)</span>;</span><br><span class="line"><span class="type">void</span>* <span class="title function_">malloc</span><span class="params">(<span class="type">uint32_t</span> size)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">free</span><span class="params">(<span class="type">void</span>* ptr)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/syscall.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//无参数的系统调用</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall0(NUMBER)({ \</span></span><br><span class="line"><span class="meta">    int retval;             \</span></span><br><span class="line"><span class="meta">    asm volatile(           \</span></span><br><span class="line"><span class="meta">        <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">        : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">        : <span class="string">"a"</span> (NUMBER)      \</span></span><br><span class="line"><span class="meta">        : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );                      \</span></span><br><span class="line"><span class="meta">    retval;                 \</span></span><br><span class="line"><span class="meta">})</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//1个参数的系统调用 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall1(NUMBER, ARG1) ({ \</span></span><br><span class="line"><span class="meta">        int retval;             \</span></span><br><span class="line"><span class="meta">        asm volatile (          \</span></span><br><span class="line"><span class="meta">            <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">            : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">            : <span class="string">"a"</span> (NUMBER), <span class="string">"b"</span> (ARG1) \</span></span><br><span class="line"><span class="meta">            : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );      \</span></span><br><span class="line"><span class="meta">    retval; \</span></span><br><span class="line"><span class="meta">})</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//2个参数的系统调用 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall2(NUMBER, ARG1, ARG2) ({ \</span></span><br><span class="line"><span class="meta">        int retval;             \</span></span><br><span class="line"><span class="meta">        asm volatile (          \</span></span><br><span class="line"><span class="meta">            <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">            : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">            : <span class="string">"a"</span> (NUMBER), <span class="string">"b"</span> (ARG1), <span class="string">"c"</span> (ARG2) \</span></span><br><span class="line"><span class="meta">            : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );      \</span></span><br><span class="line"><span class="meta">    retval; \</span></span><br><span class="line"><span class="meta">})</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//3个参数的系统调用 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _syscall3(NUMBER, ARG1, ARG2, ARG3) ({ \</span></span><br><span class="line"><span class="meta">        int retval;             \</span></span><br><span class="line"><span class="meta">        asm volatile (          \</span></span><br><span class="line"><span class="meta">            <span class="string">"int $0x80"</span>         \</span></span><br><span class="line"><span class="meta">            : <span class="string">"=a"</span> (retval)     \</span></span><br><span class="line"><span class="meta">            : <span class="string">"a"</span> (NUMBER), <span class="string">"b"</span> (ARG1), <span class="string">"c"</span> (ARG2), <span class="string">"d"</span> (ARG3) \</span></span><br><span class="line"><span class="meta">            : <span class="string">"memory"</span>          \</span></span><br><span class="line"><span class="meta">    );      \</span></span><br><span class="line"><span class="meta">    retval; \</span></span><br><span class="line"><span class="meta">})</span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">getpid</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> _syscall0(SYS_GETPID);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">write</span><span class="params">(<span class="type">char</span>* str)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> _syscall1(SYS_WRITE,str);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span>* <span class="title function_">malloc</span><span class="params">(<span class="type">uint32_t</span> size)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">void</span>*)_syscall1(SYS_MALLOC,size);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">free</span><span class="params">(<span class="type">void</span>* ptr)</span></span><br><span class="line">{</span><br><span class="line">    _syscall1(SYS_FREE,ptr);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后是更新数组<code>syscall_table</code></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/userprog/syscall-init.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> syscall_nr 32 </span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span>* syscall; </span><br><span class="line">syscall syscall_table[syscall_nr];      <span class="comment">//存储系统调用的函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回当前任务的 pid */</span> </span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">sys_getpid</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="keyword">return</span> running_thread()-&gt;pid; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">sys_write</span><span class="params">(<span class="type">char</span>* str)</span></span><br><span class="line">{</span><br><span class="line">    console_put_str(str);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">strlen</span>(str);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化系统调用 */</span> </span><br><span class="line"><span class="type">void</span> <span class="title function_">syscall_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    put_str(<span class="string">"syscall_init start\n"</span>); </span><br><span class="line">    syscall_table[SYS_GETPID] = sys_getpid; <span class="comment">//获取pid</span></span><br><span class="line">    syscall_table[SYS_WRITE] = sys_write;   <span class="comment">//write</span></span><br><span class="line">    syscall_table[SYS_MALLOC] = sys_malloc;   <span class="comment">//write</span></span><br><span class="line">    syscall_table[SYS_FREE] = sys_free;   <span class="comment">//write</span></span><br><span class="line">    put_str(<span class="string">"syscall_init done\n"</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>下面来测试一下吧:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"process.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   put_str(<span class="string">"I am kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   intr_enable();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_a</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">void</span>* addr1 = sys_malloc(<span class="number">256</span>);</span><br><span class="line">   <span class="type">void</span>* addr2 = sys_malloc(<span class="number">255</span>);</span><br><span class="line">   <span class="type">void</span>* addr3 = sys_malloc(<span class="number">254</span>);</span><br><span class="line">   console_put_str(<span class="string">" thread_a malloc addr:0x"</span>);</span><br><span class="line">   console_put_int((<span class="type">int</span>)addr1);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="type">int</span>)addr2);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="type">int</span>)addr3);</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line"></span><br><span class="line">   <span class="type">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   sys_free(addr1);</span><br><span class="line">   sys_free(addr2);</span><br><span class="line">   sys_free(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_b</span><span class="params">(<span class="type">void</span>* arg)</span> {     </span><br><span class="line">   <span class="type">void</span>* addr1 = sys_malloc(<span class="number">256</span>);</span><br><span class="line">   <span class="type">void</span>* addr2 = sys_malloc(<span class="number">255</span>);</span><br><span class="line">   <span class="type">void</span>* addr3 = sys_malloc(<span class="number">254</span>);</span><br><span class="line">   console_put_str(<span class="string">" thread_b malloc addr:0x"</span>);</span><br><span class="line">   console_put_int((<span class="type">int</span>)addr1);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="type">int</span>)addr2);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="type">int</span>)addr3);</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line"></span><br><span class="line">   <span class="type">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   sys_free(addr1);</span><br><span class="line">   sys_free(addr2);</span><br><span class="line">   sys_free(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_a</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="type">void</span>* addr1 = <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line">   <span class="type">void</span>* addr2 = <span class="built_in">malloc</span>(<span class="number">255</span>);</span><br><span class="line">   <span class="type">void</span>* addr3 = <span class="built_in">malloc</span>(<span class="number">254</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" prog_a malloc addr:0x%x,0x%x,0x%x\n"</span>, (<span class="type">int</span>)addr1, (<span class="type">int</span>)addr2, (<span class="type">int</span>)addr3);</span><br><span class="line"></span><br><span class="line">   <span class="type">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   <span class="built_in">free</span>(addr1);</span><br><span class="line">   <span class="built_in">free</span>(addr2);</span><br><span class="line">   <span class="built_in">free</span>(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">u_prog_b</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line">   <span class="type">void</span>* addr1 = <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line">   <span class="type">void</span>* addr2 = <span class="built_in">malloc</span>(<span class="number">255</span>);</span><br><span class="line">   <span class="type">void</span>* addr3 = <span class="built_in">malloc</span>(<span class="number">254</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" prog_b malloc addr:0x%x,0x%x,0x%x\n"</span>, (<span class="type">int</span>)addr1, (<span class="type">int</span>)addr2, (<span class="type">int</span>)addr3);</span><br><span class="line"></span><br><span class="line">   <span class="type">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   <span class="built_in">free</span>(addr1);</span><br><span class="line">   <span class="built_in">free</span>(addr2);</span><br><span class="line">   <span class="built_in">free</span>(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><blockquote><p>本次 main.c 中启了 4 个任务，分别是 2 个用户进程和 2 个内核线程。它们分别都申请了 256、255、254 字节大小的内存，因此它们对应的内存块规格都应该是 256 字节。所有<strong>内核线程共享内存空间，因此线程函数 k_thread_a 和 k_thread_b 所申请的内存应该会有地址累加的情况</strong>。用户进程拥有独立的内存空间，因此在申请内存时，都会从自己的堆空间从头算起，并不会产生地址累加的情况。256 的十六进制形式是0x100，比较方便查看地址累加的情况，这就是我们选择规格为 256 字节内存块的原因</p></blockquote><p>下面是正确运行结果:</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">prog_a mal1oc addr:0x804800C,Ox804810C,Ox804820C</span><br><span class="line">prog_b malloc addr:Ox804800C,0x804810C,0x804820C</span><br><span class="line">thread_a malloc addr:0xC013400C,C013410C,C013420C</span><br><span class="line">thread_b malloc addr:0xC013430C,C013440C,C013450C</span><br></pre></td></tr></table></figure></div><p>终于，内存分配到此结束~</p><hr><h2 id="D-编写硬盘驱动程序"><a href="#D-编写硬盘驱动程序" class="headerlink" title="D 编写硬盘驱动程序"></a>D 编写硬盘驱动程序</h2><p>之前我们写的东西一直都在第一课配环境创建的硬盘中，我们和书中一样，把那个作为启动盘，仅仅存放内核，现在我们再创建一个80M的硬盘来存储文件系统</p><hr><h3 id="D-1-硬盘及分区表"><a href="#D-1-硬盘及分区表" class="headerlink" title="D.1 硬盘及分区表"></a>D.1 硬盘及分区表</h3><p>创建硬盘还是利用<code>bochs</code>的命令<code>bximage</code>，此命令在<code>bochs</code>安装目<code>/bin/</code>下</p><h4 id="D1-1-创建从盘及获取安装的磁盘数"><a href="#D1-1-创建从盘及获取安装的磁盘数" class="headerlink" title="D1.1 创建从盘及获取安装的磁盘数"></a>D1.1 创建从盘及获取安装的磁盘数</h4><p>下面给出步骤(原书中图片–画横线的位置是要输入的内容–回车用中文表示)</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/Embedded_1/word_00001_070.webp" width="700"><p>然后会得到一串内容<code>ata0-master: type=disk, path="hd80M.img", mode=flat, cylinders=162, heads=16, spt=63</code>，我们需要添加到配置文件中，同时要把<code>ata0-master</code>(主盘)修改为<code>ata0-slave</code>(从盘)，其他的参数不变</p><p>修改后的文件如下:</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">###############################################</span></span><br><span class="line"><span class="comment"># Configuration file for Bochs </span></span><br><span class="line"><span class="comment">###############################################</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第一步，首先设置 Bochs 在运行过程中能够使用的内存，本例为 32MB。</span></span><br><span class="line"><span class="comment"># 关键字为：megs </span></span><br><span class="line">megs: 32 </span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二步，设置对应真实机器的 BIOS 和 VGA BIOS。</span></span><br><span class="line"><span class="comment"># 对应两个关键字为：romimage 和 vgaromimage </span></span><br><span class="line">romimage: file=/home/mouse/OS_mouse/tool/bochs/share/bochs/BIOS-bochs-latest </span><br><span class="line">vgaromimage: file=/home/mouse/OS_mouse/tool/bochs/share/bochs/VGABIOS-lgpl-latest </span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三步，设置 Bochs 所使用的磁盘，软盘的关键字为 floppy。</span></span><br><span class="line"><span class="comment"># 若只有一个软盘，则使用 floppya 即可，若有多个，则为 floppya，floppyb…</span></span><br><span class="line"><span class="comment"># floppya: 1_44=a.img, status=inserted </span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第四步，选择启动盘符。</span></span><br><span class="line"><span class="comment">#boot: floppy   #默认从软盘启动，将其注释</span></span><br><span class="line">boot: disk      <span class="comment">#改为从硬盘启动。我们的任何代码都将直接写在硬盘上，所以不会再有读写软盘的操作。</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第五步，设置日志文件的输出。</span></span><br><span class="line"><span class="built_in">log</span>: bochs.out </span><br><span class="line"></span><br><span class="line"><span class="comment"># 第六步，开启或关闭某些功能。</span></span><br><span class="line"><span class="comment"># 下面是关闭鼠标，并打开键盘。</span></span><br><span class="line">mouse: enabled=0 </span><br><span class="line">keyboard_mapping: enabled=1, map=/home/mouse/OS_mouse/tool/bochs/share/bochs/keymaps/x11-pc-us.map </span><br><span class="line"></span><br><span class="line"><span class="comment"># 硬盘设置</span></span><br><span class="line">ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 </span><br><span class="line">ata0-master: <span class="built_in">type</span>=disk, path=<span class="string">"hd60M.img"</span>, mode=flat, cylinders=121, heads=16, spt=63</span><br><span class="line">ata0-slave: <span class="built_in">type</span>=disk, path=<span class="string">"hd80M.img"</span>, mode=flat, cylinders=162, heads=16, spt=63</span><br><span class="line"></span><br><span class="line"><span class="comment"># 下面的是增加的 bochs 对 gdb 的支持，这样 gdb 便可以远程连接到此机器的 1234 端口调试了</span></span><br><span class="line"><span class="comment"># gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 </span></span><br><span class="line"><span class="comment">################### 配置文件结束 #####################</span></span><br></pre></td></tr></table></figure></div><p>在修改上面文件之前，我们可以通过指令来验证是否添加成功,因为硬盘是通过BIOS来识别的</p><p>添加之前，通过指令<code>xp/b 0x475</code>来读取磁盘的数量，再修改文件之前，输出的结果应该是<code>0x01</code>,添加之后如果数字变成离开<code>0x02</code>，就说明添加成功啦</p><p>ps：这是通过BIOS来识别写入的，所以要先<code>c</code>，运行BIOS程序之后，才能通过指令读取，否则读出来肯定全都是0x00</p><hr><h4 id="D1-2-创建磁盘分区表及简介"><a href="#D1-2-创建磁盘分区表及简介" class="headerlink" title="D1.2 创建磁盘分区表及简介"></a>D1.2 创建磁盘分区表及简介</h4><blockquote><p>文件系统是运行在操作系统中的软件模块，是操作系统提供的一套管理磁盘文件读写的方法和数据组织、存储形式，因此，文件系统=数据结构+算法，哈哈，所以它是<strong>程序</strong>。它的管理对象是文件，管辖范围是分区，因此它建立在分区的基础上，每个分区都可以有不同的文件系统</p></blockquote><p>本节的任务是将刚刚创建的磁盘进行分区，使用<code>fdisk</code>工具</p><blockquote><p>（1）硬盘容量=单片容量×磁头数。<br>（2）单片容量=每磁道扇区数×磁道数×512 字节。<br>磁道数又等于柱面数，因此将公式 2 代入公式 1 后：<br>硬盘容量=每磁道扇区数×柱面数×512 字节×磁头数<br>ata0-slave: type=disk, path=”hd80M.img”, mode=flat, cylinders=162（柱面数）, heads=16(磁头), spt=63(每磁道扇区数)</p></blockquote><p>书中这里介绍了磁盘的具体组成和分区的详细内容，这里就只简略写几句</p><blockquote><p>分区是逻辑上划分磁盘空间的方式，归根结底是人为地将硬盘上的柱面扇区划分成不同的分组，每个分组都是单独的分区。各分区都有“描述符”来描述分区本身所在硬盘上的起止界限等信息，在硬盘的MBR 中有个 64 字节“固定大小”的数据结构,这就是著名的分区表，分区表中的每个表项就是一个分区的“描述符”，表项大小是 16 字节，因此 64 字节的分区表总共可容纳 4 个表项，这就是为什么硬盘仅支持 4 个分区的原因(当初硬盘制造者认为，一台机器上顶多安装 4 个操作系统，每个操作系统各占 1 个分区，所以硬盘支持 4 个分区足矣)……….</p></blockquote><p>下面来实操一下分区:</p><p>因为fdisk的版本有所不同，所以这里需要指定兼容模式，并且打开柱面设置才可以与书中展示的一致,整体命令如下:</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line">fdisk -c=<span class="string">"dos"</span> -u=<span class="string">"cylinders"</span>  ./hd80M.img  <span class="comment">#以兼容模式进入</span></span><br><span class="line"></span><br><span class="line">x           <span class="comment">#进入专家模式</span></span><br><span class="line">c           <span class="comment">#设置柱面</span></span><br><span class="line">162         <span class="comment">#设置为162</span></span><br><span class="line">h           <span class="comment">#设置磁头</span></span><br><span class="line">16          <span class="comment">#设置为16</span></span><br><span class="line">r           <span class="comment">#返回上一级菜单</span></span><br><span class="line"></span><br><span class="line">n           <span class="comment">#创建分区</span></span><br><span class="line">p           <span class="comment">#创建主分区</span></span><br><span class="line">1           <span class="comment">#指定主分区号</span></span><br><span class="line">1           <span class="comment">#起始柱面，默认值1</span></span><br><span class="line">32          <span class="comment">#末尾柱面</span></span><br><span class="line"></span><br><span class="line">n           <span class="comment">#创建分区</span></span><br><span class="line">e           <span class="comment">#创建拓展分区</span></span><br><span class="line">4           <span class="comment">#指定拓展分区号</span></span><br><span class="line">33          <span class="comment">#起始柱面，默认值33</span></span><br><span class="line">162         <span class="comment">#末尾柱面,默认值162</span></span><br><span class="line"></span><br><span class="line">p           <span class="comment">#查看当前分区</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#设备         启动 Start 末尾 柱面  Size Id 类型</span></span><br><span class="line"><span class="comment">#./hd80M.img1          1   32   32 15.7M 83 Linux</span></span><br><span class="line"><span class="comment">#./hd80M.img4         33  162  131   64M  5 扩展</span></span><br><span class="line"></span><br><span class="line">n           <span class="comment">#创建分区</span></span><br><span class="line">l           <span class="comment">#分区满了，默认创建逻辑分区(如果没有选项就不用输入这个指令)</span></span><br><span class="line">33          <span class="comment">#逻辑分区起始柱面</span></span><br><span class="line">50          <span class="comment">#逻辑分区末尾柱面</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#.....后面同理，分别创建51~75 76~90 91~120 121~162四个逻辑分区</span></span><br><span class="line">n</span><br><span class="line">51</span><br><span class="line">75</span><br><span class="line">n</span><br><span class="line">76</span><br><span class="line">90</span><br><span class="line">n</span><br><span class="line">91</span><br><span class="line">120</span><br><span class="line">n</span><br><span class="line">121</span><br><span class="line">162</span><br><span class="line"></span><br><span class="line">p           <span class="comment">#查看分区</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#设备         启动 Start 末尾 柱面  Size Id 类型</span></span><br><span class="line"><span class="comment">#./hd80M.img1          1   32   32 15.7M 83 Linux</span></span><br><span class="line"><span class="comment">#./hd80M.img4         33  162  131   64M  5 扩展</span></span><br><span class="line"><span class="comment">#./hd80M.img5         33   50   18  8.8M 83 Linux</span></span><br><span class="line"><span class="comment">#./hd80M.img6         51   75   25 12.3M 83 Linux</span></span><br><span class="line"><span class="comment">#./hd80M.img7         76   90   15  7.4M 83 Linux</span></span><br><span class="line"><span class="comment">#./hd80M.img8         91  120   30 14.8M 83 Linux</span></span><br><span class="line"><span class="comment">#./hd80M.img9        121  162   42 20.7M 83 Linux</span></span><br><span class="line"></span><br><span class="line">l           <span class="comment">#查看已知的文件系统</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#......略</span></span><br><span class="line"></span><br><span class="line">t           <span class="comment">#设置分区id</span></span><br><span class="line">5           <span class="comment">#选择id</span></span><br><span class="line">66          <span class="comment">#自己定义的类型</span></span><br><span class="line">6           <span class="comment">#选择id</span></span><br><span class="line">66          <span class="comment">#自己定义的类型</span></span><br><span class="line">7           <span class="comment">#选择id</span></span><br><span class="line">66          <span class="comment">#自己定义的类型</span></span><br><span class="line">8           <span class="comment">#选择id</span></span><br><span class="line">66          <span class="comment">#自己定义的类型</span></span><br><span class="line">9           <span class="comment">#选择id</span></span><br><span class="line">66          <span class="comment">#自己定义的类型</span></span><br><span class="line"></span><br><span class="line">p           <span class="comment">#打印分区表</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#Disk ./hd80M.img: 79.8 MiB, 83607552 bytes, 163296 sectors</span></span><br><span class="line"><span class="comment">#Geometry: 16 heads, 63 sectors/track, 162 cylinders</span></span><br><span class="line"><span class="comment">#Units: cylinders of 1008 * 512 = 516096 bytes</span></span><br><span class="line"><span class="comment">#Sector size (logical/physical): 512 bytes / 512 bytes</span></span><br><span class="line"><span class="comment">#I/O size (minimum/optimal): 512 bytes / 512 bytes</span></span><br><span class="line"><span class="comment">#Disklabel type: dos</span></span><br><span class="line"><span class="comment">#Disk identifier: 0xfbedafc1</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#设备         启动 Start 末尾 柱面  Size Id 类型</span></span><br><span class="line"><span class="comment">#./hd80M.img1          1   32   32 15.7M 83 Linux</span></span><br><span class="line"><span class="comment">#./hd80M.img4         33  162  131   64M  5 扩展</span></span><br><span class="line"><span class="comment">#./hd80M.img5         33   50   18  8.8M 66 未知</span></span><br><span class="line"><span class="comment">#./hd80M.img6         51   75   25 12.3M 66 未知</span></span><br><span class="line"><span class="comment">#./hd80M.img7         76   90   15  7.4M 66 未知</span></span><br><span class="line"><span class="comment">#./hd80M.img8         91  120   30 14.8M 66 未知</span></span><br><span class="line"><span class="comment">#./hd80M.img9        121  162   42 20.7M 66 未知</span></span><br><span class="line"></span><br><span class="line">w           <span class="comment">#将分区表写入磁盘并退出fdisk</span></span><br><span class="line"></span><br><span class="line">fdisk -l hd80M.img  <span class="comment">#查看分区，结果应该和上面的一致</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>磁盘分区表（Disk Partition Table）简称 DPT，是由多个分区元信息汇成的表，表中每一个表项都对应一个分区，主要记录各分区的起始扇区地址，大小界限等</p><blockquote><p>分区表是由分区工具如 fdisk 创建的，但却是给操作系统使用的。听我这么一说，大伙儿不要误以为操作系统很“弱”，其实操作系统也可以创建分区表，它有底层硬件的一切操作权限，无所不能，只是在通常情况下操作系统直接安装在某个分区上，所以分区表要在内核安装之前建立好，因此分区工具通常独立于操作系统。有了分区表，操作系统（的文件系统）可以根据各表项中的信息对硬盘进行分区管理，只要按照表项中的信息访问磁盘，就不会出现分区越界的情况。分区表既然称为“表”，这表示各个表项的数据结构一致，因此磁盘分区表就是个数组，此数组长度固定为 4，数组元素是分区元信息的结构</p></blockquote><p>最初的磁盘分区表位于 MBR 引导扇区中,下面是它的结构:</p><p>（1）<strong>主引导记录 MBR</strong> –(偏移 0～0x1BD 的空间,共计 446 字节大小)<br>（2）<strong>磁盘分区表 DPT</strong> –(偏移 0x1BE～0x1FD 的空间,共64个字节)<br>（3）<strong>结束魔数 55AA</strong>，表示此扇区为主引导扇区，里面包含控制程序</p><ul><li><strong>拓展分区</strong></li></ul><blockquote><p>来解决原来所支持的分区数太少的问题。大伙儿知道，“分区表的长度固定”才是问题的症结所在。按理来说，扩展分区中包含多少个逻辑分区，扩展分区的分区表中就该有多少表项，可是任何时候新事物的发展都要把“向上兼容”当成头等大事，它既要兼容此固定长度为 4 个分区的分区表，又要突破固定分区数的限制，这似乎有点为难，该怎样设计扩展分区的分区表呢？一个两全其美的方案是视这个扩展分区为总扩展分区，将它划分成多个子扩展分区，<strong>每个子扩展分区“在逻辑上”相当于硬盘，因此每个子扩展分区都可以有 1 个分区表</strong>。这样一来，各个分区表的长度依然固定为 4，但是允许有无限多个分区表，分区表项多了，自然支持的分区数就多了。如何将这些分区表组织到一起呢？<strong>扩展分区表采用链式结构，将所有子扩展分区的分区表串在一起，形成可容纳无限个分区表的单向链表</strong>。链表是要有结点的，这里的每个分区表就是结点，一般的链表结点除了包括数据外，还必须要包括下一个结点的地址，<strong>分区表也采用了这种结构，其表项就分为两部分，一部分是描述逻辑分区的信息，另一部分是描述下一个子扩展分区的地址</strong></p></blockquote><ul><li><strong>逻辑分区</strong></li></ul><blockquote><p>要想使用分区，就离不开分区表，逻辑分区也是分区，为了使用它，也需要有元信息来描述它的范围、边界、类型等信息，因此在子扩展分区中也要有分区表来描述这些逻辑分区。分区表本身也要在子扩展分区中占磁盘空间，因此实际情况是每个子扩展分区的空间并不是只有逻辑分区，在个子扩展分区中最开始的扇区（剧透一下，此扇区称为 EBR 引导扇区，马上要介绍它）用于存储此子扩展分区中的分区表，<strong>此扇区中的内容也是前 446 字节是引导程序，中间 64 字节是分区表，后 2 字节是 0x55 和 0xAA，您看，它同 MBR 引导扇区的结构相同</strong>。紧随其后的是空闲的一部分扇区，其余剩下的大部分扇区才被用作存储数据的分区，即逻辑分区。因此，<strong>子扩展分区包含逻辑分区</strong></p></blockquote><blockquote><p><strong>扩展分区被划分出多个子扩展分区，每个子扩展分区都有自己的分区表，所以子扩展分区在逻辑上相当于单独的硬盘，各分区表在各个子扩展分区最开始的扇区中，该扇区同MBR 引导扇区结构相同，由于是经扩展分区划分出来的，所以它们称为 EBR，即扩展引导记录</strong></p></blockquote><p>EBR 中分区表的<strong>第一分区表项用来描述所包含的逻辑分区的元信息</strong>，<strong>第二分区表项用来描述下一个子扩展分区的地址</strong>，<strong>第三、四表项未用到</strong>。位于 EBR 中的分区表相当于链表中的结点，第一个分区表项存的是分区数据，第二个分区表项存的是后继分区的指针<br>值得一提的是这两个分区表项都是指向一个分区的起始，起始地址都是个扇区地址，只不过<strong>第一个分区表项指向的是该逻辑分区最开始的扇区，此扇区称为操作系统引导扇区，即 OBR 引导扇区</strong>。第二个分区表项指向下一个子扩展分区的 EBR 引导扇区</p><blockquote><p>子扩展分区是在总扩展分区中创建的，子扩展分区的偏移扇区理应以总扩展分区的绝对扇区 LBA 地址为基准，因此，“子扩展分区的绝对扇区 LBA 地址=总扩展分区绝对扇区 LBA 地址+子扩展分区的偏移扇区”。</p></blockquote><blockquote><p> 逻辑分区是在子扩展分区中创建的，逻辑分区的偏移扇区理应以子扩展分区的绝对扇区 LBA 地址为基准，因此，“逻辑分区的绝对扇区 LBA 地址=子扩展分区绝对扇区 LBA 地址+逻辑分区偏移扇区”。这里的子扩展分区就是当前子扩展分区</p></blockquote><p>书中有配图，可以更好的理解，这里就不多说了，理论部分到此为止，写写代码吧:</p><hr><h3 id="D-2-硬盘初始化"><a href="#D-2-硬盘初始化" class="headerlink" title="D.2 硬盘初始化"></a>D.2 硬盘初始化</h3><p>首先是打开硬盘的中断信号(8259A从片的IRQ14上)，主盘和从盘共享同一个中断接口，通过设置硬盘控制器的 device 寄存器中第 4 位的 dev 位指定是哪个触发</p><blockquote><p>第 2 个 ata 通道接在 8259A 从片的 IRQ15 上，该 ata 通道上可支持两个硬盘。来自 8259A从片的中断是由 8259A 主片帮忙向处理器传达的，8259A 从片是级联在 8259A 主片的 IRQ2 接口的，因此为了让处理器也响应来自 8259A 从片的中断，屏蔽中断寄存器必须也把 IRQ2 打开</p></blockquote><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化可编程中断控制器 8259A */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">pic_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/*初始化主片 */</span> </span><br><span class="line">    outb (PIC_M_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x20</span>); <span class="comment">// ICW2: 起始中断向量号为 0x20 </span></span><br><span class="line">    <span class="comment">//也就是 IR[0-7] 为 0x20 ~ 0x27 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x04</span>); <span class="comment">// ICW3: IR2 接从片</span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line">    <span class="comment">/*初始化从片 */</span> </span><br><span class="line">    outb (PIC_S_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x28</span>); <span class="comment">// ICW2: 起始中断向量号为 0x28 </span></span><br><span class="line">    <span class="comment">// 也就是 IR[8-15]为 0x28 ~ 0x2F </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x02</span>); <span class="comment">// ICW3: 设置从片连接到主片的 IR2 引脚</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* IRQ2 用于级联从片，必须打开，否则无法响应从片上的中断</span></span><br><span class="line"><span class="comment">     * 主片上打开的中断有 IRQ0 的时钟，IRQ1 的键盘和级联从片的 IRQ2,</span></span><br><span class="line"><span class="comment">     * 其他全部关闭 </span></span><br><span class="line"><span class="comment">     */</span> </span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0xf8</span>); </span><br><span class="line">    <span class="comment">// 打开从片上的 IRQ14,此引脚接收硬盘控制器的中断</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0xbf</span>);</span><br><span class="line"></span><br><span class="line">    put_str(<span class="string">" pic_init done\n"</span>); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略.......</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后为了更方便实现调试输出，所以这里实现内核使用的printf,也就是<code>printk</code>,我们将它定义在<code>lib/kernel/stdio-kernel.c</code>中,同时，这里将<code>va_end</code>等宏移动到<code>stdio.h</code>文件中，方便这个文件调用，否则编译会有问题</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/stdio-kernel.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_KERNEL_STDIO_KERNEL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_KERNEL_STDIO_KERNEL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//格式化输出字符format</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">printk</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* format,...)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/stdio-kernel.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio-kernel.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//格式化输出字符format</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">printk</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* format,...)</span></span><br><span class="line">{</span><br><span class="line">    va_list args;   </span><br><span class="line">    va_start(args,format);      <span class="comment">//使args指向format的栈地址</span></span><br><span class="line">    <span class="type">char</span> buf[<span class="number">1024</span>] = {<span class="number">0</span>};       <span class="comment">//用于存储拼接后的字符串</span></span><br><span class="line">    <span class="built_in">vsprintf</span>(buf,format,args);  <span class="comment">//转换可变参</span></span><br><span class="line">    va_end(args);               <span class="comment">//清理原指针</span></span><br><span class="line">    console_put_str(buf);       <span class="comment">//通过中断输出</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>下面介绍和硬盘相关的数据结构了，它定义在<code>device/ide.h</code>中</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/ide.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __DEVICE_IDE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __DEVICE_IDE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 分区结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">partition</span> {</span></span><br><span class="line">   <span class="type">uint32_t</span> start_lba;    <span class="comment">// 起始扇区</span></span><br><span class="line">   <span class="type">uint32_t</span> sec_cnt;    <span class="comment">// 扇区数</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">my_disk</span>;</span>    <span class="comment">// 分区所属的硬盘</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">part_tag</span>;</span>   <span class="comment">// 用于队列中的标记</span></span><br><span class="line">   <span class="type">char</span> name[<span class="number">8</span>];        <span class="comment">// 分区名称</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>* <span class="title">sb</span>;</span>    <span class="comment">// 本分区的超级块</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">block_bitmap</span>;</span>  <span class="comment">// 块位图</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">inode_bitmap</span>;</span>  <span class="comment">// i结点位图</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">open_inodes</span>;</span>    <span class="comment">// 本分区打开的i结点队列</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">disk</span> {</span></span><br><span class="line">   <span class="type">char</span> name[<span class="number">8</span>];        <span class="comment">// 本硬盘的名称，如sda等</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">my_channel</span>;</span>  <span class="comment">// 此块硬盘归属于哪个ide通道</span></span><br><span class="line">   <span class="type">uint8_t</span> dev_no;        <span class="comment">// 本硬盘是主0还是从1</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span> <span class="title">prim_parts</span>[4];</span>  <span class="comment">// 主分区顶多是4个</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span> <span class="title">logic_parts</span>[8];</span> <span class="comment">// 逻辑分区数量无限,但总得有个支持的上限,那就支持8个</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ata通道结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> {</span></span><br><span class="line">   <span class="type">char</span> name[<span class="number">8</span>];        <span class="comment">// 本ata通道名称 </span></span><br><span class="line">   <span class="type">uint16_t</span> port_base;    <span class="comment">// 本通道的起始端口号</span></span><br><span class="line">   <span class="type">uint8_t</span> irq_no;        <span class="comment">// 本通道所用的中断号</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">lock</span>;</span>    <span class="comment">// 通道锁</span></span><br><span class="line">   <span class="type">bool</span> expecting_intr;    <span class="comment">// 表示等待硬盘的中断</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">semaphore</span> <span class="title">disk_done</span>;</span>  <span class="comment">// 用于阻塞、唤醒驱动程序</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span> <span class="title">devices</span>[2];</span>    <span class="comment">// 一个通道上连接两个硬盘，一主一从</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_init</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">uint8_t</span> channel_cnt;</span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> <span class="title">channels</span>[];</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/ide.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ide.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio-kernel.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 定义硬盘各寄存器的端口号 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_data(channel) (channel-&gt;port_base + 0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_error(channel) (channel-&gt;port_base + 1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_sect_cnt(channel) (channel-&gt;port_base + 2)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_lba_l(channel) (channel-&gt;port_base + 3)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_lba_m(channel) (channel-&gt;port_base + 4)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_lba_h(channel) (channel-&gt;port_base + 5)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_dev(channel) (channel-&gt;port_base + 6)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_status(channel) (channel-&gt;port_base + 7)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_cmd(channel) (reg_status(channel))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_alt_status(channel)  (channel-&gt;port_base + 0x206)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_ctl(channel) reg_alt_status(channel)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* reg_alt_status寄存器的一些关键位 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_STAT_BSY 0x80      <span class="comment">// 硬盘忙</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_STAT_DRDY 0x40      <span class="comment">// 驱动器准备好 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_STAT_DRQ 0x8      <span class="comment">// 数据传输准备好了</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* device寄存器的一些关键位 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_DEV_MBS0xa0    <span class="comment">// 第7位和第5位固定为1</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_DEV_LBA0x40</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_DEV_DEV0x10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 一些硬盘操作的指令 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CMD_IDENTIFY   0xec    <span class="comment">// identify指令</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CMD_READ_SECTOR   0x20     <span class="comment">// 读扇区指令</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CMD_WRITE_SECTOR   0x30    <span class="comment">// 写扇区指令</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 定义可读写的最大扇区数,调试用的 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> max_lba ((80*1024*1024/512) - 1)<span class="comment">// 只支持80MB硬盘</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">uint8_t</span> channel_cnt;            <span class="comment">// 按硬盘数计算的通道数</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> <span class="title">channels</span>[2];</span>    <span class="comment">// 有两个ide通道</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘数据结构初始化 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_init</span><span class="params">()</span> </span><br><span class="line">{</span><br><span class="line">   printk(<span class="string">"ide_init start\n"</span>);</span><br><span class="line">   <span class="type">uint8_t</span> hd_cnt = *((<span class="type">uint8_t</span>*)(<span class="number">0x475</span>));    <span class="comment">// 获取硬盘的数量(BIOS中0x475存储)</span></span><br><span class="line">   ASSERT(hd_cnt &gt; <span class="number">0</span>);</span><br><span class="line">   channel_cnt = DIV_ROUND_UP(hd_cnt, <span class="number">2</span>);    <span class="comment">// 一个ide通道上有两个硬盘,根据硬盘数量反推有几个ide通道</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span>;</span></span><br><span class="line">   <span class="type">uint8_t</span> channel_no = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 处理每个通道上的硬盘 */</span></span><br><span class="line">   <span class="keyword">while</span> (channel_no &lt; channel_cnt) </span><br><span class="line">   {</span><br><span class="line">      channel = &amp;channels[channel_no];</span><br><span class="line">      <span class="built_in">sprintf</span>(channel-&gt;name, <span class="string">"ide%d"</span>, channel_no);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 为每个ide通道初始化端口基址及中断向量 */</span></span><br><span class="line">      <span class="keyword">switch</span> (channel_no) </span><br><span class="line">      {</span><br><span class="line">    <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line">       channel-&gt;port_base   = <span class="number">0x1f0</span>;   <span class="comment">// ide0通道的起始端口号是0x1f0</span></span><br><span class="line">       channel-&gt;irq_no    = <span class="number">0x20</span> + <span class="number">14</span>;   <span class="comment">// 从片8259a上倒数第二的中断引脚,温盘,也就是ide0通道的的中断向量号</span></span><br><span class="line">       <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">       channel-&gt;port_base   = <span class="number">0x170</span>;   <span class="comment">// ide1通道的起始端口号是0x170</span></span><br><span class="line">       channel-&gt;irq_no    = <span class="number">0x20</span> + <span class="number">15</span>;   <span class="comment">// 从8259A上的最后一个中断引脚,我们用来响应ide1通道上的硬盘中断</span></span><br><span class="line">       <span class="keyword">break</span>;</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">      channel-&gt;expecting_intr = <span class="literal">false</span>;   <span class="comment">// 未向硬盘写入指令时不期待硬盘的中断</span></span><br><span class="line">      lock_init(&amp;channel-&gt;lock);     </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 初始化为0,目的是向硬盘控制器请求数据后,硬盘驱动sema_down此信号量会阻塞线程,</span></span><br><span class="line"><span class="comment">    直到硬盘完成后通过发中断,由中断处理程序将此信号量sema_up,唤醒线程. */</span></span><br><span class="line">      sema_init(&amp;channel-&gt;disk_done, <span class="number">0</span>);</span><br><span class="line">      channel_no++;   <span class="comment">// 下一个channel</span></span><br><span class="line">   }</span><br><span class="line">   printk(<span class="string">"ide_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>基础部分的数据结构到这里就初始化完了</p><hr><h3 id="D-3-实现-thread-yield-和-idle-线程"><a href="#D-3-实现-thread-yield-和-idle-线程" class="headerlink" title="D.3 实现 thread_yield 和 idle 线程"></a>D.3 实现 thread_yield 和 idle 线程</h3><p>这里还要实现一些其他的基础部件，比如<code>thread_yield</code></p><blockquote><p>thread_yield 定义在 thread.c 中，它的功能是主动把 CPU 使用权让出来，它与 thread_block 的区别是thread_yield 执行后任务的状态是 TASK_READY，即让出 CPU 后，它会被加入到就绪队列中，下次还能继续被调度器调度执行，而 thread_block 执行后任务的状态是 TASK_BLOCKED，需要被唤醒后才能加入到就绪队列，</p></blockquote><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略..........</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global"</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">idle_thread</span>;</span>    <span class="comment">//idle 线程</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//略..........</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 系统空闲时运行的线程 </span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">idle</span><span class="params">(<span class="type">void</span>* arg UNUSED)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>) </span><br><span class="line">    { </span><br><span class="line">        thread_block(TASK_BLOCKED); </span><br><span class="line">        <span class="comment">//执行 hlt 时必须要保证目前处在开中断的情况下，下面是让cpu休息的汇编指令</span></span><br><span class="line">        <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"sti; hlt"</span> : : : <span class="string">"memory"</span>)</span>; </span><br><span class="line">    } </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">// 主动让出 cpu，换其他线程运行(当前任务添加到就绪队列)</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_yield</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> =</span> running_thread(); </span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable(); </span><br><span class="line">    ASSERT(!elem_find(&amp;thread_ready_list, &amp;cur-&gt;general_tag)); </span><br><span class="line">    list_append(&amp;thread_ready_list, &amp;cur-&gt;general_tag); </span><br><span class="line">    cur-&gt;status = TASK_READY; </span><br><span class="line">    schedule(); </span><br><span class="line">    intr_set_status(old_status); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略..........</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化线程</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"thread_init start\n"</span>);</span><br><span class="line"></span><br><span class="line">    list_init(&amp;thread_ready_list);</span><br><span class="line">    list_init(&amp;thread_all_list);</span><br><span class="line">    lock_init(&amp;pid_lock);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//将当前的main函数创建为线程</span></span><br><span class="line">    make_main_thread();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建 idle 线程 (当就绪队列没有任务的时候运行)</span></span><br><span class="line">    idle_thread = thread_start(<span class="string">"idle"</span>, <span class="number">10</span>, idle, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    put_str(<span class="string">"thread_init done"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><blockquote><p>添加的idle线程中有个指令hlt，这个指令的功能<strong>让处理器停止执行指令，也就是将处理器挂起</strong>（并不是类似“jmp $”那样空兜 CPU，CPU 利用率 100%），使处理器得到休息，CPU 利用率一下子就掉下来了，在那一小段时间 CPU 利用率为 0。处理器已经停止运行，因此并不会再产生内部异常，<strong>唯一能唤醒处理器的就是外部中断</strong>，当外部发生后，处理器恢复执行后面的指令。处理器需要被唤醒，必须要保证在开中断的情况下执行 hlt，因此内联汇编代码中，先执行 sti，再执行 hlt。顺便说一下，idle_thread 在第一次创建时会被加入到就绪队列，<strong>因此会执行一次，然后阻塞</strong></p></blockquote><p>同时，这里用了一个宏，定义在<code>global.h</code>文件中，用来防止编译器警告的(未使用的变量)</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> UNUSED __attribute__ ((unused))</span></span><br></pre></td></tr></table></figure></div><hr><h3 id="D-4-实现简单的休眠"><a href="#D-4-实现简单的休眠" class="headerlink" title="D.4 实现简单的休眠"></a>D.4 实现简单的休眠</h3><p>这里还是基础部件,实现简单的休眠</p><blockquote><p>硬盘和 CPU 是相互独立的个体，它们各自并行执行，但由于硬盘是低速设备，其在处理请求时往往消耗很长的时间（不过手册上说最慢的情况也能在 31 秒之内完成），为避免浪费 CPU 资源，在等待硬盘操作的过程中最好把 CPU 主动让出来，让 CPU 去执行其他任务，为实现这种“明智”的行为，我们在 timer.c中定义休眠函数，当然这只是简易版，精度不是很高，能达到目的就可以了</p></blockquote><p>这里的休眠实现就是通过不断的调用<code>thread_yield</code>，将自己添加到就绪列表中，直到达到休眠的时间再继续于运行后面的代码</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/timer.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"timer.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IRQ0_FREQUENCY 100          <span class="comment">//100HZ</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> INPUT_FREQUENCY 1193180     <span class="comment">//计数器0的工作脉冲信号频率</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY     <span class="comment">//通过宏来计算初值</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONTRER0_PORT 0x40      <span class="comment">//端口2</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER0_NO 0           <span class="comment">//计数器0</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER_MODE 2          <span class="comment">//工作模式 方式2</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> READ_WRITE_LATCH 3      <span class="comment">//读写方式，3表示先读写低8位，再读写高8位</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIT_CONTROL_PORT 0x43   <span class="comment">//控制字寄存器的端口</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> mil_seconds_per_intr (1000 / IRQ0_FREQUENCY)    <span class="comment">//这里计算出来就是10ms一次中断</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> ticks;                 <span class="comment">//ticks是内核自中断开启以来的总共的滴答数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 把操作的计数器 counter_no､ 读写锁属性 rwl､ 计数器模式 counter_mode 写入模式控制寄存器并赋予初始值 counter_value </span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">frequency_set</span><span class="params">(<span class="type">uint8_t</span> counter_port,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> counter_no,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> rwl,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> counter_mode,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint16_t</span> counter_value)</span> </span><br><span class="line">{ </span><br><span class="line"><span class="comment">//往控制字寄存器端口 0x43 中写入控制字</span></span><br><span class="line">    outb(PIT_CONTROL_PORT, (<span class="type">uint8_t</span>)(counter_no &lt;&lt; <span class="number">6</span> | rwl &lt;&lt; <span class="number">4</span> | counter_mode &lt;&lt; <span class="number">1</span>)); </span><br><span class="line"><span class="comment">//先写入 counter_value 的低 8 位</span></span><br><span class="line">    outb(counter_port, (<span class="type">uint8_t</span>)counter_value); </span><br><span class="line"><span class="comment">//再写入 counter_value 的高 8 位 </span></span><br><span class="line">    outb(counter_port, (<span class="type">uint8_t</span>)counter_value &gt;&gt; <span class="number">8</span>); </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">//时钟的中断函数</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">intr_timer_handler</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur_thread</span> =</span> running_thread(); <span class="comment">//获取当前正在运行的线程，将其复制给PCB指针cur_thread</span></span><br><span class="line"></span><br><span class="line">    ASSERT(cur_thread-&gt;stack_magic == <span class="number">0x22332233</span>);      <span class="comment">//检查堆栈是否溢出</span></span><br><span class="line"></span><br><span class="line">    cur_thread-&gt;elapsed_ticks++;                        <span class="comment">//记录此线程占用cpu的时间</span></span><br><span class="line">    ticks++;                                            <span class="comment">//内核从第一次进入中断至今的时间</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(cur_thread-&gt;ticks == <span class="number">0</span>)                          <span class="comment">//进程的时间片用完，则开始调度新的进程上cpu</span></span><br><span class="line">    {</span><br><span class="line">        schedule();     <span class="comment">//调度器</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        cur_thread-&gt;ticks--;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//以 tick 为单位的 sleep，任何时间形式的 sleep 会转换此 ticks 形式 </span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">ticks_to_sleep</span><span class="params">(<span class="type">uint32_t</span> sleep_ticks)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">uint32_t</span> start_tick = ticks;    <span class="comment">//开始休眠的ticks</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//若间隔的 ticks 数不够便让出 cpu </span></span><br><span class="line">    <span class="keyword">while</span> (ticks - start_tick &lt; sleep_ticks) </span><br><span class="line">    { </span><br><span class="line">        thread_yield(); </span><br><span class="line">    } </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">//以毫秒为单位的 sleep 1 秒= 1000 毫秒</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mtime_sleep</span><span class="params">(<span class="type">uint32_t</span> m_seconds)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">uint32_t</span> sleep_ticks = DIV_ROUND_UP(m_seconds, mil_seconds_per_intr); </span><br><span class="line">    ASSERT(sleep_ticks &gt; <span class="number">0</span>); </span><br><span class="line">    ticks_to_sleep(sleep_ticks); </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化 PIT8253</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">timer_init</span><span class="params">()</span> </span><br><span class="line">{ </span><br><span class="line">    put_str(<span class="string">"timer_init start\n"</span>); </span><br><span class="line">    <span class="comment">//设置 8253 的定时周期，也就是发中断的周期</span></span><br><span class="line">    frequency_set(CONTRER0_PORT, \</span><br><span class="line">                    COUNTER0_NO, \</span><br><span class="line">                    READ_WRITE_LATCH,\</span><br><span class="line">                    COUNTER_MODE, \</span><br><span class="line">                    COUNTER0_VALUE); </span><br><span class="line"></span><br><span class="line">    register_handler(<span class="number">0x20</span>,intr_timer_handler);     <span class="comment">//注册时钟的中断处理函数</span></span><br><span class="line">    put_str(<span class="string">"timer_init done\n"</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/timer.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __TIMER_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __TIMER_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定时器PIT8253的初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">timer_init</span><span class="params">()</span>;</span><br><span class="line"><span class="comment">//以毫秒为单位的 sleep 1 秒= 1000 毫秒</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mtime_sleep</span><span class="params">(<span class="type">uint32_t</span> m_seconds)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><hr><h3 id="D-5-完善硬盘驱动程序-获取硬盘信息，扫描分区表"><a href="#D-5-完善硬盘驱动程序-获取硬盘信息，扫描分区表" class="headerlink" title="D.5 完善硬盘驱动程序 & 获取硬盘信息，扫描分区表"></a>D.5 完善硬盘驱动程序 &amp; 获取硬盘信息，扫描分区表</h3><p>下面是驱动的完整部分，因为涉及大量书中的概念等，这里只给出代码和测试结果:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/ide.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __DEVICE_IDE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __DEVICE_IDE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 分区结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">partition</span> {</span></span><br><span class="line">   <span class="type">uint32_t</span> start_lba; <span class="comment">// 起始扇区</span></span><br><span class="line">   <span class="type">uint32_t</span> sec_cnt; <span class="comment">// 扇区数</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">my_disk</span>;</span> <span class="comment">// 分区所属的硬盘</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">part_tag</span>;</span> <span class="comment">// 用于队列中的标记</span></span><br><span class="line">   <span class="type">char</span> name[<span class="number">8</span>]; <span class="comment">// 分区名称</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>* <span class="title">sb</span>;</span> <span class="comment">// 本分区的超级块</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">block_bitmap</span>;</span> <span class="comment">// 块位图</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">inode_bitmap</span>;</span> <span class="comment">// i结点位图</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">open_inodes</span>;</span> <span class="comment">// 本分区打开的i结点队列</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">disk</span> {</span></span><br><span class="line">   <span class="type">char</span> name[<span class="number">8</span>];   <span class="comment">// 本硬盘的名称，如sda等</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">my_channel</span>;</span>   <span class="comment">// 此块硬盘归属于哪个ide通道</span></span><br><span class="line">   <span class="type">uint8_t</span> dev_no;   <span class="comment">// 本硬盘是主0还是从1</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span> <span class="title">prim_parts</span>[4];</span>   <span class="comment">// 主分区顶多是4个</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span> <span class="title">logic_parts</span>[8];</span>   <span class="comment">// 逻辑分区数量无限,但总得有个支持的上限,那就支持8个</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ata通道结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> {</span></span><br><span class="line">   <span class="type">char</span> name[<span class="number">8</span>]; <span class="comment">// 本ata通道名称, 如ata0,也被叫做ide0. 可以参考bochs配置文件中关于硬盘的配置。</span></span><br><span class="line">   <span class="type">uint16_t</span> port_base; <span class="comment">// 本通道的起始端口号</span></span><br><span class="line">   <span class="type">uint8_t</span> irq_no; <span class="comment">// 本通道所用的中断号</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">lock</span>;</span></span><br><span class="line">   <span class="type">bool</span> expecting_intr; <span class="comment">// 向硬盘发完命令后等待来自硬盘的中断</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">semaphore</span> <span class="title">disk_done</span>;</span> <span class="comment">// 硬盘处理完成.线程用这个信号量来阻塞自己，由硬盘完成后产生的中断将线程唤醒</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span> <span class="title">devices</span>[2];</span> <span class="comment">// 一个通道上连接两个硬盘，一主一从</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">intr_hd_handler</span><span class="params">(<span class="type">uint8_t</span> irq_no)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_init</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">uint8_t</span> channel_cnt;</span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> <span class="title">channels</span>[];</span></span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">partition_list</span>;</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_read</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">uint32_t</span> lba, <span class="type">void</span>* buf, <span class="type">uint32_t</span> sec_cnt)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_write</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">uint32_t</span> lba, <span class="type">void</span>* buf, <span class="type">uint32_t</span> sec_cnt)</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/ide.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ide.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio-kernel.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"timer.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 定义硬盘各寄存器的端口号 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_data(channel) (channel-&gt;port_base + 0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_error(channel) (channel-&gt;port_base + 1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_sect_cnt(channel) (channel-&gt;port_base + 2)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_lba_l(channel) (channel-&gt;port_base + 3)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_lba_m(channel) (channel-&gt;port_base + 4)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_lba_h(channel) (channel-&gt;port_base + 5)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_dev(channel) (channel-&gt;port_base + 6)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_status(channel) (channel-&gt;port_base + 7)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_cmd(channel) (reg_status(channel))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_alt_status(channel)  (channel-&gt;port_base + 0x206)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> reg_ctl(channel) reg_alt_status(channel)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* reg_status寄存器的一些关键位 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_STAT_BSY 0x80      <span class="comment">// 硬盘忙</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_STAT_DRDY 0x40      <span class="comment">// 驱动器准备好 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_STAT_DRQ 0x8      <span class="comment">// 数据传输准备好了</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* device寄存器的一些关键位 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_DEV_MBS0xa0    <span class="comment">// 第7位和第5位固定为1</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_DEV_LBA0x40</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT_DEV_DEV0x10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 一些硬盘操作的指令 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CMD_IDENTIFY   0xec    <span class="comment">// identify指令</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CMD_READ_SECTOR   0x20     <span class="comment">// 读扇区指令</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CMD_WRITE_SECTOR   0x30    <span class="comment">// 写扇区指令</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 定义可读写的最大扇区数,调试用的 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> max_lba ((80*1024*1024/512) - 1)<span class="comment">// 只支持80MB硬盘</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">uint8_t</span> channel_cnt;   <span class="comment">// 按硬盘数计算的通道数</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> <span class="title">channels</span>[2];</span> <span class="comment">// 有两个ide通道</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 用于记录总扩展分区的起始lba,初始为0,partition_scan时以此为标记 */</span></span><br><span class="line"><span class="type">int32_t</span> ext_lba_base = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">uint8_t</span> p_no = <span class="number">0</span>, l_no = <span class="number">0</span>; <span class="comment">// 用来记录硬盘主分区和逻辑分区的下标</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">partition_list</span>;</span> <span class="comment">// 分区队列</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 构建一个16字节大小的结构体,用来存分区表项 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">partition_table_entry</span> {</span></span><br><span class="line">   <span class="type">uint8_t</span>  bootable; <span class="comment">// 是否可引导</span></span><br><span class="line">   <span class="type">uint8_t</span>  start_head; <span class="comment">// 起始磁头号</span></span><br><span class="line">   <span class="type">uint8_t</span>  start_sec; <span class="comment">// 起始扇区号</span></span><br><span class="line">   <span class="type">uint8_t</span>  start_chs; <span class="comment">// 起始柱面号</span></span><br><span class="line">   <span class="type">uint8_t</span>  fs_type; <span class="comment">// 分区类型</span></span><br><span class="line">   <span class="type">uint8_t</span>  end_head; <span class="comment">// 结束磁头号</span></span><br><span class="line">   <span class="type">uint8_t</span>  end_sec; <span class="comment">// 结束扇区号</span></span><br><span class="line">   <span class="type">uint8_t</span>  end_chs; <span class="comment">// 结束柱面号</span></span><br><span class="line"><span class="comment">/* 更需要关注的是下面这两项 */</span></span><br><span class="line">   <span class="type">uint32_t</span> start_lba; <span class="comment">// 本分区起始扇区的lba地址</span></span><br><span class="line">   <span class="type">uint32_t</span> sec_cnt; <span class="comment">// 本分区的扇区数目</span></span><br><span class="line">} __attribute__ ((packed)); <span class="comment">// 保证此结构是16字节大小</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 引导扇区,mbr或ebr所在的扇区 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">boot_sector</span> {</span></span><br><span class="line">   <span class="type">uint8_t</span>  other[<span class="number">446</span>]; <span class="comment">// 引导代码</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span>   <span class="title">partition_table_entry</span> <span class="title">partition_table</span>[4];</span>       <span class="comment">// 分区表中有4项,共64字节</span></span><br><span class="line">   <span class="type">uint16_t</span> signature; <span class="comment">// 启动扇区的结束标志是0x55,0xaa,</span></span><br><span class="line">} __attribute__ ((packed));</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 选择读写的硬盘 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">select_disk</span><span class="params">(<span class="keyword">struct</span> disk* hd)</span> {</span><br><span class="line">   <span class="type">uint8_t</span> reg_device = BIT_DEV_MBS | BIT_DEV_LBA;</span><br><span class="line">   <span class="keyword">if</span> (hd-&gt;dev_no == <span class="number">1</span>) {<span class="comment">// 若是从盘就置DEV位为1</span></span><br><span class="line">      reg_device |= BIT_DEV_DEV;</span><br><span class="line">   }</span><br><span class="line">   outb(reg_dev(hd-&gt;my_channel), reg_device);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 向硬盘控制器写入起始扇区地址及要读写的扇区数 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">select_sector</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">uint32_t</span> lba, <span class="type">uint8_t</span> sec_cnt)</span> {</span><br><span class="line">   ASSERT(lba &lt;= max_lba);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span> =</span> hd-&gt;my_channel;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 写入要读写的扇区数*/</span></span><br><span class="line">   outb(reg_sect_cnt(channel), sec_cnt); <span class="comment">// 如果sec_cnt为0,则表示写入256个扇区</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 写入lba地址(即扇区号) */</span></span><br><span class="line">   outb(reg_lba_l(channel), lba); <span class="comment">// lba地址的低8位,不用单独取出低8位.outb函数中的汇编指令outb %b0, %w1会只用al。</span></span><br><span class="line">   outb(reg_lba_m(channel), lba &gt;&gt; <span class="number">8</span>); <span class="comment">// lba地址的8~15位</span></span><br><span class="line">   outb(reg_lba_h(channel), lba &gt;&gt; <span class="number">16</span>); <span class="comment">// lba地址的16~23位</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 因为lba地址的24~27位要存储在device寄存器的0～3位,</span></span><br><span class="line"><span class="comment">    * 无法单独写入这4位,所以在此处把device寄存器再重新写入一次*/</span></span><br><span class="line">   outb(reg_dev(channel), BIT_DEV_MBS | BIT_DEV_LBA | (hd-&gt;dev_no == <span class="number">1</span> ? BIT_DEV_DEV : <span class="number">0</span>) | lba &gt;&gt; <span class="number">24</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 向通道channel发命令cmd */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">cmd_out</span><span class="params">(<span class="keyword">struct</span> ide_channel* channel, <span class="type">uint8_t</span> cmd)</span> {</span><br><span class="line"><span class="comment">/* 只要向硬盘发出了命令便将此标记置为true,硬盘中断处理程序需要根据它来判断 */</span></span><br><span class="line">   channel-&gt;expecting_intr = <span class="literal">true</span>;</span><br><span class="line">   outb(reg_cmd(channel), cmd);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘读入sec_cnt个扇区的数据到buf */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">read_from_sector</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">void</span>* buf, <span class="type">uint8_t</span> sec_cnt)</span> {</span><br><span class="line">   <span class="type">uint32_t</span> size_in_byte;</span><br><span class="line">   <span class="keyword">if</span> (sec_cnt == <span class="number">0</span>) {</span><br><span class="line">   <span class="comment">/* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */</span></span><br><span class="line">      size_in_byte = <span class="number">256</span> * <span class="number">512</span>;</span><br><span class="line">   } <span class="keyword">else</span> { </span><br><span class="line">      size_in_byte = sec_cnt * <span class="number">512</span>; </span><br><span class="line">   }</span><br><span class="line">   insw(reg_data(hd-&gt;my_channel), buf, size_in_byte / <span class="number">2</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将buf中sec_cnt扇区的数据写入硬盘 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">write2sector</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">void</span>* buf, <span class="type">uint8_t</span> sec_cnt)</span> {</span><br><span class="line">   <span class="type">uint32_t</span> size_in_byte;</span><br><span class="line">   <span class="keyword">if</span> (sec_cnt == <span class="number">0</span>) {</span><br><span class="line">   <span class="comment">/* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */</span></span><br><span class="line">      size_in_byte = <span class="number">256</span> * <span class="number">512</span>;</span><br><span class="line">   } <span class="keyword">else</span> { </span><br><span class="line">      size_in_byte = sec_cnt * <span class="number">512</span>; </span><br><span class="line">   }</span><br><span class="line">   outsw(reg_data(hd-&gt;my_channel), buf, size_in_byte / <span class="number">2</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 等待30秒 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">bool</span> <span class="title function_">busy_wait</span><span class="params">(<span class="keyword">struct</span> disk* hd)</span> {</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span> =</span> hd-&gt;my_channel;</span><br><span class="line">   <span class="type">uint16_t</span> time_limit = <span class="number">30</span> * <span class="number">1000</span>;     <span class="comment">// 可以等待30000毫秒</span></span><br><span class="line">   <span class="keyword">while</span> (time_limit -= <span class="number">10</span> &gt;= <span class="number">0</span>) {</span><br><span class="line">      <span class="keyword">if</span> (!(inb(reg_status(channel)) &amp; BIT_STAT_BSY)) {</span><br><span class="line"> <span class="keyword">return</span> (inb(reg_status(channel)) &amp; BIT_STAT_DRQ);</span><br><span class="line">      } <span class="keyword">else</span> {</span><br><span class="line"> mtime_sleep(<span class="number">10</span>);     <span class="comment">// 睡眠10毫秒</span></span><br><span class="line">      }</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从硬盘读取sec_cnt个扇区到buf */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_read</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">uint32_t</span> lba, <span class="type">void</span>* buf, <span class="type">uint32_t</span> sec_cnt)</span> {   <span class="comment">// 此处的sec_cnt为32位大小</span></span><br><span class="line">   ASSERT(lba &lt;= max_lba);</span><br><span class="line">   ASSERT(sec_cnt &gt; <span class="number">0</span>);</span><br><span class="line">   lock_acquire (&amp;hd-&gt;my_channel-&gt;lock);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 1 先选择操作的硬盘 */</span></span><br><span class="line">   select_disk(hd);</span><br><span class="line"></span><br><span class="line">   <span class="type">uint32_t</span> secs_op; <span class="comment">// 每次操作的扇区数</span></span><br><span class="line">   <span class="type">uint32_t</span> secs_done = <span class="number">0</span>; <span class="comment">// 已完成的扇区数</span></span><br><span class="line">   <span class="keyword">while</span>(secs_done &lt; sec_cnt) {</span><br><span class="line">      <span class="keyword">if</span> ((secs_done + <span class="number">256</span>) &lt;= sec_cnt) {</span><br><span class="line"> secs_op = <span class="number">256</span>;</span><br><span class="line">      } <span class="keyword">else</span> {</span><br><span class="line"> secs_op = sec_cnt - secs_done;</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 2 写入待读入的扇区数和起始扇区号 */</span></span><br><span class="line">      select_sector(hd, lba + secs_done, secs_op);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 3 执行的命令写入reg_cmd寄存器 */</span></span><br><span class="line">      cmd_out(hd-&gt;my_channel, CMD_READ_SECTOR);      <span class="comment">// 准备开始读数据</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/*********************   阻塞自己的时机  ***********************</span></span><br><span class="line"><span class="comment">      在硬盘已经开始工作(开始在内部读数据或写数据)后才能阻塞自己,现在硬盘已经开始忙了,</span></span><br><span class="line"><span class="comment">      将自己阻塞,等待硬盘完成读操作后通过中断处理程序唤醒自己*/</span></span><br><span class="line">      sema_down(&amp;hd-&gt;my_channel-&gt;disk_done);</span><br><span class="line">   <span class="comment">/*************************************************************/</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 4 检测硬盘状态是否可读 */</span></span><br><span class="line">      <span class="comment">/* 醒来后开始执行下面代码*/</span></span><br><span class="line">      <span class="keyword">if</span> (!busy_wait(hd)) {      <span class="comment">// 若失败</span></span><br><span class="line"> <span class="type">char</span> error[<span class="number">64</span>];</span><br><span class="line"> <span class="built_in">sprintf</span>(error, <span class="string">"%s read sector %d failed!!!!!!\n"</span>, hd-&gt;name, lba);</span><br><span class="line"> PANIC(error);</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 5 把数据从硬盘的缓冲区中读出 */</span></span><br><span class="line">      read_from_sector(hd, (<span class="type">void</span>*)((<span class="type">uint32_t</span>)buf + secs_done * <span class="number">512</span>), secs_op);</span><br><span class="line">      secs_done += secs_op;</span><br><span class="line">   }</span><br><span class="line">   lock_release(&amp;hd-&gt;my_channel-&gt;lock);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将buf中sec_cnt扇区数据写入硬盘 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_write</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">uint32_t</span> lba, <span class="type">void</span>* buf, <span class="type">uint32_t</span> sec_cnt)</span> {</span><br><span class="line">   ASSERT(lba &lt;= max_lba);</span><br><span class="line">   ASSERT(sec_cnt &gt; <span class="number">0</span>);</span><br><span class="line">   lock_acquire (&amp;hd-&gt;my_channel-&gt;lock);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 1 先选择操作的硬盘 */</span></span><br><span class="line">   select_disk(hd);</span><br><span class="line"></span><br><span class="line">   <span class="type">uint32_t</span> secs_op; <span class="comment">// 每次操作的扇区数</span></span><br><span class="line">   <span class="type">uint32_t</span> secs_done = <span class="number">0</span>; <span class="comment">// 已完成的扇区数</span></span><br><span class="line">   <span class="keyword">while</span>(secs_done &lt; sec_cnt) {</span><br><span class="line">      <span class="keyword">if</span> ((secs_done + <span class="number">256</span>) &lt;= sec_cnt) {</span><br><span class="line"> secs_op = <span class="number">256</span>;</span><br><span class="line">      } <span class="keyword">else</span> {</span><br><span class="line"> secs_op = sec_cnt - secs_done;</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 2 写入待写入的扇区数和起始扇区号 */</span></span><br><span class="line">      select_sector(hd, lba + secs_done, secs_op);      <span class="comment">// 先将待读的块号lba地址和待读入的扇区数写入lba寄存器</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 3 执行的命令写入reg_cmd寄存器 */</span></span><br><span class="line">      cmd_out(hd-&gt;my_channel, CMD_WRITE_SECTOR);      <span class="comment">// 准备开始写数据</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 4 检测硬盘状态是否可读 */</span></span><br><span class="line">      <span class="keyword">if</span> (!busy_wait(hd)) {      <span class="comment">// 若失败</span></span><br><span class="line"> <span class="type">char</span> error[<span class="number">64</span>];</span><br><span class="line"> <span class="built_in">sprintf</span>(error, <span class="string">"%s write sector %d failed!!!!!!\n"</span>, hd-&gt;name, lba);</span><br><span class="line"> PANIC(error);</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 5 将数据写入硬盘 */</span></span><br><span class="line">      write2sector(hd, (<span class="type">void</span>*)((<span class="type">uint32_t</span>)buf + secs_done * <span class="number">512</span>), secs_op);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 在硬盘响应期间阻塞自己 */</span></span><br><span class="line">      sema_down(&amp;hd-&gt;my_channel-&gt;disk_done);</span><br><span class="line">      secs_done += secs_op;</span><br><span class="line">   }</span><br><span class="line">   <span class="comment">/* 醒来后开始释放锁*/</span></span><br><span class="line">   lock_release(&amp;hd-&gt;my_channel-&gt;lock);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将dst中len个相邻字节交换位置后存入buf */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">swap_pairs_bytes</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* dst, <span class="type">char</span>* buf, <span class="type">uint32_t</span> len)</span> {</span><br><span class="line">   <span class="type">uint8_t</span> idx;</span><br><span class="line">   <span class="keyword">for</span> (idx = <span class="number">0</span>; idx &lt; len; idx += <span class="number">2</span>) {</span><br><span class="line">      <span class="comment">/* buf中存储dst中两相邻元素交换位置后的字符串*/</span></span><br><span class="line">      buf[idx + <span class="number">1</span>] = *dst++;   </span><br><span class="line">      buf[idx]     = *dst++;   </span><br><span class="line">   }</span><br><span class="line">   buf[idx] = <span class="string">'\0'</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获得硬盘参数信息 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">identify_disk</span><span class="params">(<span class="keyword">struct</span> disk* hd)</span> {</span><br><span class="line">   <span class="type">char</span> id_info[<span class="number">512</span>];</span><br><span class="line">   select_disk(hd);</span><br><span class="line">   cmd_out(hd-&gt;my_channel, CMD_IDENTIFY);</span><br><span class="line"><span class="comment">/* 向硬盘发送指令后便通过信号量阻塞自己,</span></span><br><span class="line"><span class="comment"> * 待硬盘处理完成后,通过中断处理程序将自己唤醒 */</span></span><br><span class="line">   sema_down(&amp;hd-&gt;my_channel-&gt;disk_done);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 醒来后开始执行下面代码*/</span></span><br><span class="line">   <span class="keyword">if</span> (!busy_wait(hd)) {     <span class="comment">//  若失败</span></span><br><span class="line">      <span class="type">char</span> error[<span class="number">64</span>];</span><br><span class="line">      <span class="built_in">sprintf</span>(error, <span class="string">"%s identify failed!!!!!!\n"</span>, hd-&gt;name);</span><br><span class="line">      PANIC(error);</span><br><span class="line">   }</span><br><span class="line">   read_from_sector(hd, id_info, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">   <span class="type">char</span> buf[<span class="number">64</span>];</span><br><span class="line">   <span class="type">uint8_t</span> sn_start = <span class="number">10</span> * <span class="number">2</span>, sn_len = <span class="number">20</span>, md_start = <span class="number">27</span> * <span class="number">2</span>, md_len = <span class="number">40</span>;</span><br><span class="line">   swap_pairs_bytes(&amp;id_info[sn_start], buf, sn_len);</span><br><span class="line">   printk(<span class="string">"   disk %s info:\n      SN: %s\n"</span>, hd-&gt;name, buf);</span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">   swap_pairs_bytes(&amp;id_info[md_start], buf, md_len);</span><br><span class="line">   printk(<span class="string">"      MODULE: %s\n"</span>, buf);</span><br><span class="line">   <span class="type">uint32_t</span> sectors = *(<span class="type">uint32_t</span>*)&amp;id_info[<span class="number">60</span> * <span class="number">2</span>];</span><br><span class="line">   printk(<span class="string">"      SECTORS: %d\n"</span>, sectors);</span><br><span class="line">   printk(<span class="string">"      CAPACITY: %dMB\n"</span>, sectors * <span class="number">512</span> / <span class="number">1024</span> / <span class="number">1024</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 扫描硬盘hd中地址为ext_lba的扇区中的所有分区 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">partition_scan</span><span class="params">(<span class="keyword">struct</span> disk* hd, <span class="type">uint32_t</span> ext_lba)</span> {</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">boot_sector</span>* <span class="title">bs</span> =</span> sys_malloc(<span class="keyword">sizeof</span>(<span class="keyword">struct</span> boot_sector));</span><br><span class="line">   ide_read(hd, ext_lba, bs, <span class="number">1</span>);</span><br><span class="line">   <span class="type">uint8_t</span> part_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition_table_entry</span>* <span class="title">p</span> =</span> bs-&gt;partition_table;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 遍历分区表4个分区表项 */</span></span><br><span class="line">   <span class="keyword">while</span> (part_idx++ &lt; <span class="number">4</span>) {</span><br><span class="line">      <span class="keyword">if</span> (p-&gt;fs_type == <span class="number">0x5</span>) { <span class="comment">// 若为扩展分区</span></span><br><span class="line"> <span class="keyword">if</span> (ext_lba_base != <span class="number">0</span>) { </span><br><span class="line"> <span class="comment">/* 子扩展分区的start_lba是相对于主引导扇区中的总扩展分区地址 */</span></span><br><span class="line">    partition_scan(hd, p-&gt;start_lba + ext_lba_base);</span><br><span class="line"> } <span class="keyword">else</span> { <span class="comment">// ext_lba_base为0表示是第一次读取引导块,也就是主引导记录所在的扇区</span></span><br><span class="line"> <span class="comment">/* 记录下扩展分区的起始lba地址,后面所有的扩展分区地址都相对于此 */</span></span><br><span class="line">    ext_lba_base = p-&gt;start_lba;</span><br><span class="line">    partition_scan(hd, p-&gt;start_lba);</span><br><span class="line"> }</span><br><span class="line">      } <span class="keyword">else</span> <span class="keyword">if</span> (p-&gt;fs_type != <span class="number">0</span>) { <span class="comment">// 若是有效的分区类型</span></span><br><span class="line"> <span class="keyword">if</span> (ext_lba == <span class="number">0</span>) { <span class="comment">// 此时全是主分区</span></span><br><span class="line">    hd-&gt;prim_parts[p_no].start_lba = ext_lba + p-&gt;start_lba;</span><br><span class="line">    hd-&gt;prim_parts[p_no].sec_cnt = p-&gt;sec_cnt;</span><br><span class="line">    hd-&gt;prim_parts[p_no].my_disk = hd;</span><br><span class="line">    list_append(&amp;partition_list, &amp;hd-&gt;prim_parts[p_no].part_tag);</span><br><span class="line">    <span class="built_in">sprintf</span>(hd-&gt;prim_parts[p_no].name, <span class="string">"%s%d"</span>, hd-&gt;name, p_no + <span class="number">1</span>);</span><br><span class="line">    p_no++;</span><br><span class="line">    ASSERT(p_no &lt; <span class="number">4</span>);    <span class="comment">// 0,1,2,3</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line">    hd-&gt;logic_parts[l_no].start_lba = ext_lba + p-&gt;start_lba;</span><br><span class="line">    hd-&gt;logic_parts[l_no].sec_cnt = p-&gt;sec_cnt;</span><br><span class="line">    hd-&gt;logic_parts[l_no].my_disk = hd;</span><br><span class="line">    list_append(&amp;partition_list, &amp;hd-&gt;logic_parts[l_no].part_tag);</span><br><span class="line">    <span class="built_in">sprintf</span>(hd-&gt;logic_parts[l_no].name, <span class="string">"%s%d"</span>, hd-&gt;name, l_no + <span class="number">5</span>); <span class="comment">// 逻辑分区数字是从5开始,主分区是1～4.</span></span><br><span class="line">    l_no++;</span><br><span class="line">    <span class="keyword">if</span> (l_no &gt;= <span class="number">8</span>)    <span class="comment">// 只支持8个逻辑分区,避免数组越界</span></span><br><span class="line">       <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line">      } </span><br><span class="line">      p++;</span><br><span class="line">   }</span><br><span class="line">   sys_free(bs);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 打印分区信息 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">bool</span> <span class="title function_">partition_info</span><span class="params">(<span class="keyword">struct</span> list_elem* pelem, <span class="type">int</span> arg UNUSED)</span> {</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">part</span> =</span> elem2entry(<span class="keyword">struct</span> partition, part_tag, pelem);</span><br><span class="line">   printk(<span class="string">"   %s start_lba:0x%x, sec_cnt:0x%x\n"</span>,part-&gt;name, part-&gt;start_lba, part-&gt;sec_cnt);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在此处return false与函数本身功能无关,</span></span><br><span class="line"><span class="comment"> * 只是为了让主调函数list_traversal继续向下遍历元素 */</span></span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘中断处理程序 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">intr_hd_handler</span><span class="params">(<span class="type">uint8_t</span> irq_no)</span> {</span><br><span class="line">   ASSERT(irq_no == <span class="number">0x2e</span> || irq_no == <span class="number">0x2f</span>);</span><br><span class="line">   <span class="type">uint8_t</span> ch_no = irq_no - <span class="number">0x2e</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span> =</span> &amp;channels[ch_no];</span><br><span class="line">   ASSERT(channel-&gt;irq_no == irq_no);</span><br><span class="line"><span class="comment">/* 不必担心此中断是否对应的是这一次的expecting_intr,</span></span><br><span class="line"><span class="comment"> * 每次读写硬盘时会申请锁,从而保证了同步一致性 */</span></span><br><span class="line">   <span class="keyword">if</span> (channel-&gt;expecting_intr) {</span><br><span class="line">      channel-&gt;expecting_intr = <span class="literal">false</span>;</span><br><span class="line">      sema_up(&amp;channel-&gt;disk_done);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 读取状态寄存器使硬盘控制器认为此次的中断已被处理,</span></span><br><span class="line"><span class="comment"> * 从而硬盘可以继续执行新的读写 */</span></span><br><span class="line">      inb(reg_status(channel));</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘数据结构初始化 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ide_init</span><span class="params">()</span> {</span><br><span class="line">   printk(<span class="string">"ide_init start\n"</span>);</span><br><span class="line">   <span class="type">uint8_t</span> hd_cnt = *((<span class="type">uint8_t</span>*)(<span class="number">0x475</span>));      <span class="comment">// 获取硬盘的数量</span></span><br><span class="line">   ASSERT(hd_cnt &gt; <span class="number">0</span>);</span><br><span class="line">   list_init(&amp;partition_list);</span><br><span class="line">   channel_cnt = DIV_ROUND_UP(hd_cnt, <span class="number">2</span>);   <span class="comment">// 一个ide通道上有两个硬盘,根据硬盘数量反推有几个ide通道</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span>;</span></span><br><span class="line">   <span class="type">uint8_t</span> channel_no = <span class="number">0</span>, dev_no = <span class="number">0</span>; </span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 处理每个通道上的硬盘 */</span></span><br><span class="line">   <span class="keyword">while</span> (channel_no &lt; channel_cnt) {</span><br><span class="line">      channel = &amp;channels[channel_no];</span><br><span class="line">      <span class="built_in">sprintf</span>(channel-&gt;name, <span class="string">"ide%d"</span>, channel_no);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 为每个ide通道初始化端口基址及中断向量 */</span></span><br><span class="line">      <span class="keyword">switch</span> (channel_no) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line">    channel-&gt;port_base = <span class="number">0x1f0</span>;   <span class="comment">// ide0通道的起始端口号是0x1f0</span></span><br><span class="line">    channel-&gt;irq_no = <span class="number">0x20</span> + <span class="number">14</span>;   <span class="comment">// 从片8259a上倒数第二的中断引脚,温盘,也就是ide0通道的的中断向量号</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">    channel-&gt;port_base = <span class="number">0x170</span>;   <span class="comment">// ide1通道的起始端口号是0x170</span></span><br><span class="line">    channel-&gt;irq_no = <span class="number">0x20</span> + <span class="number">15</span>;   <span class="comment">// 从8259A上的最后一个中断引脚,我们用来响应ide1通道上的硬盘中断</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">      channel-&gt;expecting_intr = <span class="literal">false</span>;   <span class="comment">// 未向硬盘写入指令时不期待硬盘的中断</span></span><br><span class="line">      lock_init(&amp;channel-&gt;lock);     </span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化为0,目的是向硬盘控制器请求数据后,硬盘驱动sema_down此信号量会阻塞线程,</span></span><br><span class="line"><span class="comment">   直到硬盘完成后通过发中断,由中断处理程序将此信号量sema_up,唤醒线程. */</span></span><br><span class="line">      sema_init(&amp;channel-&gt;disk_done, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">      register_handler(channel-&gt;irq_no, intr_hd_handler);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 分别获取两个硬盘的参数及分区信息 */</span></span><br><span class="line">      <span class="keyword">while</span> (dev_no &lt; <span class="number">2</span>) {</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">hd</span> =</span> &amp;channel-&gt;devices[dev_no];</span><br><span class="line"> hd-&gt;my_channel = channel;</span><br><span class="line"> hd-&gt;dev_no = dev_no;</span><br><span class="line"> <span class="built_in">sprintf</span>(hd-&gt;name, <span class="string">"sd%c"</span>, <span class="string">'a'</span> + channel_no * <span class="number">2</span> + dev_no);</span><br><span class="line"> identify_disk(hd); <span class="comment">// 获取硬盘参数</span></span><br><span class="line"> <span class="keyword">if</span> (dev_no != <span class="number">0</span>) { <span class="comment">// 内核本身的裸硬盘(hd60M.img)不处理</span></span><br><span class="line">    partition_scan(hd, <span class="number">0</span>);  <span class="comment">// 扫描该硬盘上的分区  </span></span><br><span class="line"> }</span><br><span class="line"> p_no = <span class="number">0</span>, l_no = <span class="number">0</span>;</span><br><span class="line"> dev_no++; </span><br><span class="line">      }</span><br><span class="line">      dev_no = <span class="number">0</span>;     <span class="comment">// 将硬盘驱动器号置0,为下一个channel的两个硬盘初始化。</span></span><br><span class="line">      channel_no++;   <span class="comment">// 下一个channel</span></span><br><span class="line">   }</span><br><span class="line"></span><br><span class="line">   printk(<span class="string">"\n   all partition info\n"</span>);</span><br><span class="line">   <span class="comment">/* 打印所有分区信息 */</span></span><br><span class="line">   list_traversal(&amp;partition_list, partition_info, (<span class="type">int</span>)<span class="literal">NULL</span>);</span><br><span class="line">   printk(<span class="string">"ide_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最后将<code>ide_init</code>函数添加到总初始化中，然后运行就可以看到扫描硬盘的结果，应该和之前创建的一致</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/init.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"timer.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"tss.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"syscall-init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"keyboard.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdio-kernel.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ide.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化所有模块</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_all</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"init_all start\n"</span>);</span><br><span class="line">    idt_init();             <span class="comment">//中断</span></span><br><span class="line">    mem_init();             <span class="comment">//内存管理初始化</span></span><br><span class="line">    thread_init();          <span class="comment">//线程初始化</span></span><br><span class="line">    timer_init();           <span class="comment">//定时器设备 PIT</span></span><br><span class="line">    keyboard_init();        <span class="comment">//键盘</span></span><br><span class="line">    tss_init();             <span class="comment">//初始化tss</span></span><br><span class="line">    syscall_init();         <span class="comment">//初始化系统调用</span></span><br><span class="line">    console_init();         <span class="comment">//初始化控制台,最好放在打开中断前</span></span><br><span class="line">    intr_enable();          <span class="comment">// 后面的ide_init需要打开中断</span></span><br><span class="line">    ide_init();            <span class="comment">// 初始化硬盘</span></span><br><span class="line">    printk(<span class="string">"init_all done\n"</span>);   <span class="comment">//不用put_str是因为当时测试printk用的，懒得改</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>运行结果如下:</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">ide_init start</span><br><span class="line">disk sda info:</span><br><span class="line">SN:BXHDOO011</span><br><span class="line">MODULE: Generic 1234</span><br><span class="line">SECTORS: 121968</span><br><span class="line">CAPACITY: 59MB</span><br><span class="line">disk sdb info:</span><br><span class="line">SN: BXHD00012</span><br><span class="line">MODULE: Generic 1234</span><br><span class="line">SECTORS: 163296</span><br><span class="line">CAPACITY: 79MB</span><br><span class="line">all partition info</span><br><span class="line">sdb1 start_lba:Ox3F, sec_cnt:Ox7DC1</span><br><span class="line">sdb5 start_Iba:Ox7E3F, sec_cnt:Ox46A1</span><br><span class="line">sdb6 start_Iba:OxC51F, sec_cnt:0x6231</span><br><span class="line">sdb7 start_Iba:0x1278F, seC_cnt:Ox3AD1</span><br><span class="line">sdb8 start_Iba:Ox1629F, sec_cnt:Ox75E1</span><br><span class="line">sdb9 start_Iba:Ox1D8BF, sec_cnt:OxA521</span><br><span class="line">ide_init <span class="keyword">done</span></span><br><span class="line">init_all <span class="keyword">done</span></span><br></pre></td></tr></table></figure></div><p>到此结束，这里涉及大量的硬盘相关概念知识在书中，大家可以自己阅读</p><hr><h2 id="E-文件系统前的小结"><a href="#E-文件系统前的小结" class="headerlink" title="E 文件系统前的小结"></a>E 文件系统前的小结</h2><p>ok呀，也是只剩下两章就要结束这本书了，学校不让我去实习我就只能窝在这里学操作系统了，太难受了……………..</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li>[操作系统真象还原 (郑纲) (Z-Library)]   — 大家可以自己在网上查找相关资源</li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#52;&#x36;&#x37;&#48;&#x32;&#x33;&#x36;&#x32;&#64;&#x71;&#x71;&#46;&#99;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">这一章主要对《操作系统真相还原》的用户进程/完善内核(系统调用，完善内存管理)/硬盘驱动相关，本篇文章主要用来记录学习过程中遇到的问题，以及相关知识概念，方便后来查询......</summary>
    
    
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/categories/OS/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/OS/Linux/"/>
    
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/tags/OS/"/>
    
    <category term="《真相还原》" scheme="https://blog.haozi-haozi.cn/tags/%E3%80%8A%E7%9C%9F%E7%9B%B8%E8%BF%98%E5%8E%9F%E3%80%8B/"/>
    
  </entry>
  
  <entry>
    <title>真象还原 --内存管理/线程/输入输出 study(3)</title>
    <link href="https://blog.haozi-haozi.cn/2025/10/28/os_elephant_three/"/>
    <id>https://blog.haozi-haozi.cn/2025/10/28/os_elephant_three/</id>
    <published>2025-10-28T11:02:00.000Z</published>
    <updated>2026-03-24T08:59:32.036Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A 前情提要"></a>A 前情提要</h2><p>在此之前，第一部分我们完成了基础的<strong>环境配置，bochs配置</strong>，以及<strong>MBR,loader</strong>的的基础编写，成功的进入了<strong>保护模式</strong>并且开启了<strong>内存分页功能</strong></p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/21/os_elephant_one/">真象还原 –环境/准备 study(1)</a></li></ul></blockquote><p>第二部分完成了对<strong>内联汇编</strong>，<strong>中断初始化</strong>，<strong>定时器初始化</strong>，也实现了<em>基础打印函数</em>*</p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/24/os_elephant_two/">真象还原 –内核/中断 study(2)</a></li></ul></blockquote><p>这一部分主要学习<strong>内存管理/线程/同步</strong>相关内容</p><p><strong>ps:如果参考本系列文章来实操，需要结合《操作系统真象还原》一起观看，否则会缺失很多细节</strong></p><hr><h2 id="B-实现-assert-断言"><a href="#B-实现-assert-断言" class="headerlink" title="B 实现 assert 断言"></a>B 实现 assert 断言</h2><p>断言其实就是在 C 语言中学过的 <strong>assert</strong></p><blockquote><p>在我们系统中，我们实现两种断言，一种是为内核系统使用的ASSERT，另一种是为用户进程使用的assert，用户进程离现在还早，咱们本节先实现专供内核使用的 ASSERT。</p></blockquote><blockquote><p>一方面，当内核运行中出现问题时，多属于严重的错误，着实没必要再运行下去了。另一方面，断言在输出报错信息时，屏幕输出不应该被其他进程干扰，这样咱们才能专注于报错信息。综上两点原因，</p></blockquote><blockquote><p>ASSERT 排查出错误后，最好在关中断的情况下打印报错信息。内核运行时，为了通过时钟中断定时调度其他任务，大部分情况下中断是打开的，如何在开中断的情况下把中断关闭,这就是我们等会要实现的内容</p></blockquote><p><strong>首先实现开关中断</strong>，这里我们在<code>interrupt.c</code>和<code>interrupt.h</code>中添加:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __INTERRUPUT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __INTERRUPUT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span>* intr_handler;</span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*定义中断的两种状态</span></span><br><span class="line"><span class="comment"> *  INTR_OFF 值为0，表示关中断</span></span><br><span class="line"><span class="comment"> *  INTR_ON  值为1，表示开中断</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span>{</span>   <span class="comment">//中断状态</span></span><br><span class="line">    INTR_OFF,</span><br><span class="line">    INTR_ON</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_enable</span><span class="params">()</span>;              <span class="comment">/*开中断并且返回开中断前的状态*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_disable</span><span class="params">()</span>;             <span class="comment">/*关中断并且返回关中断前的状态 */</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_get_status</span><span class="params">()</span>;          <span class="comment">/*获取当前中断状态*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_set_status</span><span class="params">(<span class="keyword">enum</span> intr_status status)</span>;       <span class="comment">/*将中断状态设置成 status*/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span>              <span class="comment">//put_str</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span>                 <span class="comment">//io操作,联合汇编</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_CNT 0x33       <span class="comment">// 目前总共支持的中断数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_CTRL 0x20         <span class="comment">// 主片的控制端口是 0x20 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_DATA 0x21         <span class="comment">// 主片的数据端口是 0x21 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_CTRL 0xa0         <span class="comment">// 从片的控制端口是 0xa0 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_DATA 0xa1         <span class="comment">// 从片的数据端口是 0xa1</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_IF 0X00000200    <span class="comment">//eflags 寄存器的 if 位为0</span></span></span><br><span class="line"><span class="comment">//获取 eflags 寄存器的值,EFLAG_VAR 是返回值</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GET_EFLAGS(EFLAG_VAR)   asm volatile(<span class="string">"pushfl; popl %0"</span> : <span class="string">"=g"</span> (EFLAG_VAR))</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 中断门描述符结构体</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span>  <span class="title">gate_desc</span>{</span></span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_low_word;</span><br><span class="line">    <span class="type">uint16_t</span>    selector;</span><br><span class="line">    <span class="type">uint8_t</span>     dcount;     <span class="comment">//此项为双字计数字段,是门描述符中的第4字节。为固定值,不用考虑</span></span><br><span class="line">    <span class="type">uint8_t</span>     attribute;</span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_high_word;     </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态函数声明,非必须</span></span><br><span class="line"><span class="comment">// intr_handler 是自己定义的(interrupt.h) typedef void* intr_handler；</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span>;</span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">gate_desc</span> <span class="title">idt</span>[<span class="title">IDT_DESC_CNT</span>];</span>              <span class="comment">// idt是中断描述符,本质上一个中断门描述符数组</span></span><br><span class="line"><span class="keyword">extern</span> intr_handler intr_entry_table[IDT_DESC_CNT];     <span class="comment">// 声明引用定义kernel.S中的中断处理函数入口函数 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">//添加部分:</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exception_init</span><span class="params">(<span class="type">void</span>)</span>;                   <span class="comment">//完成一般中断处理函数注册及异常名称注册</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">general_intr_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span>;   <span class="comment">//通用的中断处理函数,一般在异常出现时处理</span></span><br><span class="line"><span class="type">const</span> <span class="type">char</span>* intr_name[IDT_DESC_CNT];                <span class="comment">//用于保留异常的名字</span></span><br><span class="line">intr_handler idt_table[IDT_DESC_CNT];               <span class="comment">//中断处理函数程序数组,在kernel.S 中定义的 intrXXentry是中断处理函数的入口,最终调用 idt_table 里面的函数</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化可编程中断控制器 8259A */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">pic_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/*初始化主片 */</span> </span><br><span class="line">    outb (PIC_M_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x20</span>); <span class="comment">// ICW2: 起始中断向量号为 0x20 </span></span><br><span class="line">    <span class="comment">//也就是 IR[0-7] 为 0x20 ～ 0x27 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x04</span>); <span class="comment">// ICW3: IR2 接从片</span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line">    <span class="comment">/*初始化从片 */</span> </span><br><span class="line">    outb (PIC_S_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x28</span>); <span class="comment">// ICW2: 起始中断向量号为 0x28 </span></span><br><span class="line">    <span class="comment">// 也就是 IR[8-15]为 0x28 ～ 0x2F </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x02</span>); <span class="comment">// ICW3: 设置从片连接到主片的 IR2 引脚</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/*打开主片上 IR0,也就是目前只接受时钟产生的中断 */</span> </span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0xfe</span>); </span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0xff</span>); </span><br><span class="line">    put_str(<span class="string">" pic_init done\n"</span>); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*创建中断门描述符*/</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 参数: 中断门描述符的指针,中断描述符内的属性,中断描述符内对应的中断处理函数</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span></span><br><span class="line">{</span><br><span class="line">    p_gdesc-&gt;func_offset_low_word = (<span class="type">uint32_t</span>)function &amp; <span class="number">0x0000FFFF</span>;</span><br><span class="line">    p_gdesc-&gt;selector = SELECTOR_K_CODE;        <span class="comment">//定义在global.h : 指向内核数据段的选择子</span></span><br><span class="line">    p_gdesc-&gt;dcount = <span class="number">0</span>;</span><br><span class="line">    p_gdesc-&gt;attribute = attr;</span><br><span class="line">    p_gdesc-&gt;func_offset_high_word = ((<span class="type">uint32_t</span>)function &amp; <span class="number">0Xffff0000</span>) &gt;&gt;<span class="number">16</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*初始化中断描述符*/</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">idt_desc_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        make_idt_desc(&amp;idt[i],IDT_DESC_ATTR_DPL0,intr_entry_table[i]);</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"idt_desc_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//添加部分</span></span><br><span class="line"><span class="comment">//通用的中断处理函数,一般在异常出现时处理</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">general_intr_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(vec_nr == <span class="number">0x27</span> || vec_nr == <span class="number">0x2f</span>)    <span class="comment">//IRQ7s和IRQ15会产生伪中断,无需处理,0x2f是从片8259A上的最后一个IRQ引脚,保留项</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"int vector : 0x"</span>);</span><br><span class="line">    put_int(vec_nr);</span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//页中断,我当时调试使用的,大家可以不用管</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">page_fault_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(vec_nr == <span class="number">0x27</span> || vec_nr == <span class="number">0x2f</span>)    <span class="comment">//IRQ7s和IRQ15会产生伪中断,无需处理,0x2f是从片8259A上的最后一个IRQ引脚,保留项</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"int vector : 0x"</span>);</span><br><span class="line">    put_int(vec_nr);</span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> faulting_address;</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"mov %%cr2, %0"</span> : <span class="string">"=r"</span> (faulting_address))</span>; <span class="comment">// 读取CR2</span></span><br><span class="line">    put_str(<span class="string">"Page fault at address: 0x"</span>);</span><br><span class="line">    put_int(faulting_address);  <span class="comment">// 十六进制打印</span></span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//完成一般中断处理函数注册及异常名称注册</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exception_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">/* idt_table 数组中的函数是在进入中断后根据中断向量号调用的 */</span></span><br><span class="line">        <span class="comment">/* 见 kernel/kernel.S 的 call [idt_table + %1*4] */</span> </span><br><span class="line">        idt_table[i] = general_intr_handler;    <span class="comment">// 默认为 general_intr_handler ,以后会由 register_handler 来注册具体处理函数</span></span><br><span class="line">        intr_name[i] = <span class="string">"unknown"</span>;               <span class="comment">// 先统一赋值为 unknow</span></span><br><span class="line">    }</span><br><span class="line">    idt_table[<span class="number">14</span>] = page_fault_handler;         <span class="comment">//页错误处理函数</span></span><br><span class="line">    intr_name[<span class="number">0</span>] = <span class="string">"#DE Divide Error"</span>; </span><br><span class="line">    intr_name[<span class="number">1</span>] = <span class="string">"#DB Debug Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">2</span>] = <span class="string">"NMI Interrupt"</span>; </span><br><span class="line">    intr_name[<span class="number">3</span>] = <span class="string">"#BP Breakpoint Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">4</span>] = <span class="string">"#OF Overflow Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">5</span>] = <span class="string">"#BR BOUND Range Exceeded Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">6</span>] = <span class="string">"#UD Invalid Opcode Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">7</span>] = <span class="string">"#NM Device Not Available Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">8</span>] = <span class="string">"#DF Double Fault Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">9</span>] = <span class="string">"Coprocessor Segment Overrun"</span>; </span><br><span class="line">    intr_name[<span class="number">10</span>] = <span class="string">"#TS Invalid TSS Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">11</span>] = <span class="string">"#NP Segment Not Present"</span>;</span><br><span class="line">    intr_name[<span class="number">12</span>] = <span class="string">"#SS Stack Fault Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">13</span>] = <span class="string">"#GP General Protection Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">14</span>] = <span class="string">"#PF Page-Fault Exception"</span>; </span><br><span class="line">    <span class="comment">// intr_name[15] 第 15 项是 intel 保留项,未使用</span></span><br><span class="line">    intr_name[<span class="number">16</span>] = <span class="string">"#MF x87 FPU Floating-Point Error"</span>; </span><br><span class="line">    intr_name[<span class="number">17</span>] = <span class="string">"#AC Alignment Check Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">18</span>] = <span class="string">"#MC Machine-Check Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">19</span>] = <span class="string">"#XF SIMD Floating-Point Exception"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*开中断并且返回开中断前的状态 intr_statusl类型定义再interrupt.h中 */</span></span><br><span class="line"><span class="comment">/*这里在每个if分支都添加return语句，可以让程序更加快，但更大，空间换时间*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_enable</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span>;</span>    </span><br><span class="line">    <span class="keyword">if</span>(INTR_ON == intr_get_status())</span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_ON;</span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_OFF;</span><br><span class="line">        <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"sti"</span>)</span>;    <span class="comment">//开中断,sti指令将IF置1</span></span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*关中断并且返回关中断前的状态 */</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_disable</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span>;</span></span><br><span class="line">    <span class="keyword">if</span>(INTR_ON == intr_get_status())</span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_ON;</span><br><span class="line">        <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"cli"</span> : : : <span class="string">"memory"</span>)</span>; <span class="comment">//关中断,cli指令将IF位置置0</span></span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_OFF;</span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*将中断状态设置成 status*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_set_status</span><span class="params">(<span class="keyword">enum</span> intr_status status)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> (status &amp; INTR_ON) ?intr_enable() : intr_disable();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*获取当前中断状态*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_get_status</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span> eflags = <span class="number">0</span>;</span><br><span class="line">    GET_EFLAGS(eflags);     <span class="comment">//宏实现</span></span><br><span class="line">    <span class="keyword">return</span> (EFLAGS_IF &amp; eflags) ? INTR_ON : INTR_OFF;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*完成有关中断的所有初始化工作*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"idt_init start\n"</span>);</span><br><span class="line">    idt_desc_init();                <span class="comment">//初始化中断描述符表</span></span><br><span class="line">    exception_init();               <span class="comment">//初始化异常名并注册通常的中断处理函数</span></span><br><span class="line">    pic_init();                     <span class="comment">//初始化8259A</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载idt</span></span><br><span class="line">    <span class="type">uint64_t</span> idt_operand = ((<span class="keyword">sizeof</span>(idt) - <span class="number">1</span>) | ((<span class="type">uint64_t</span>)((<span class="type">uint32_t</span>)idt &lt;&lt; <span class="number">16</span>)));</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"lidt %0"</span> ::<span class="string">""</span>(idt_operand))</span>;</span><br><span class="line">    put_str(<span class="string">"idt_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p><strong>然后来实现断言功能</strong>: 我们来仿照C语言来实现,ASSERT(条件表达式) 如果表达式成立则什么都不做，如果不成立则打印出错信息并停止执行，在路径<code>/mouse/kernel</code>下添加<code>debug.c</code> <code>debug.h</code>文件</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/debug.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __KERNEL_DEBUG_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __KERNEL_DEBUG_H</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">panic_spin</span><span class="params">(<span class="type">char</span>* filename,<span class="type">int</span> line,<span class="type">const</span> <span class="type">char</span>* func,<span class="type">const</span> <span class="type">char</span>* condition)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*************************** __VA_ARGS__ ******************************* </span></span><br><span class="line"><span class="comment">* __VA_ARGS__ 是预处理器所支持的专用标识符</span></span><br><span class="line"><span class="comment">* 代表所有与省略号相对应的参数。</span></span><br><span class="line"><span class="comment">* "..."表示定义的宏其参数可变。*/</span> </span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PANIC(...) panic_spin (__FILE__, __LINE__, __func__, __VA_ARGS__) </span></span><br><span class="line"><span class="comment">/***********************************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> NDEBUG</span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> ASSERT(CONDITION)   ((VOID) 0 )         <span class="comment">//定义为空，相当于关闭这个宏</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span>                           <span class="comment">/* 符号#让编译器将宏的参数转化为字符串字面量 */</span> </span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> ASSERT(CONDITION)  <span class="keyword">if</span>(CONDITION){} <span class="keyword">else</span>{ PANIC(#CONDITION); }</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/*NDEBUG*/</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span>  <span class="comment">/*__KERNEL_DEBUG_H*/</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/debug.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*打印文件名，行号，函数名，条件并使程序悬停*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">panic_spin</span><span class="params">(<span class="type">char</span>* filename,<span class="type">int</span> line,<span class="type">const</span> <span class="type">char</span>* func,<span class="type">const</span> <span class="type">char</span>* condition)</span></span><br><span class="line">{</span><br><span class="line">    intr_disable();     <span class="comment">//关闭中断，因为有的时候会单独调用panic_spin</span></span><br><span class="line">    put_str(<span class="string">"\n\n\n!!!!! error !!!!!\n"</span>);</span><br><span class="line">    put_str(<span class="string">"filename:"</span>); put_str((<span class="type">char</span>*)filename);put_str(<span class="string">"\n"</span>);</span><br><span class="line">    put_str(<span class="string">"line:0x"</span>);put_int(line);put_str(<span class="string">"\n"</span>);</span><br><span class="line">    put_str(<span class="string">"function:"</span>); put_str((<span class="type">char</span>*)func);put_str(<span class="string">"\n"</span>);</span><br><span class="line">    put_str(<span class="string">"condition:"</span>);put_str((<span class="type">char</span>*)condition);put_str(<span class="string">"\n"</span>);</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最后修改主函数，主动进入断言:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">// while(1);</span></span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有中断</span></span><br><span class="line">    <span class="comment">// asm volatile("sti") ;        //暂时打开中断,这里先不打开</span></span><br><span class="line">    ASSERT(<span class="number">1</span> == <span class="number">2</span>);                 <span class="comment">//测试断言</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);                       <span class="comment">//防止退出</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后<code>make</code> <code>make img</code> 运行就能看到相关提示了,比如函数名，错误行号，错误的内容等</p><hr><h2 id="C-实现字符串操作函数"><a href="#C-实现字符串操作函数" class="headerlink" title="C 实现字符串操作函数"></a>C 实现字符串操作函数</h2><p>为了后面的开发更加方便，这里实现与字符串相关的函数，此后这里的函数会被经常用到</p><p>这里在lib文件下建立 string.c文件，Makefile 文件和之前一样，已经在上一章中介绍过了,注意解除Makefile里面的注释，让编译器编译string.c文件</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将dst_起始的size个字节置为value */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">memset</span><span class="params">(<span class="type">void</span>* dst_, <span class="type">uint8_t</span> value, <span class="type">uint32_t</span> size)</span> {</span><br><span class="line">   ASSERT(dst_ != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="type">uint8_t</span>* dst = (<span class="type">uint8_t</span>*)dst_;</span><br><span class="line">   <span class="keyword">while</span> (size-- &gt; <span class="number">0</span>)</span><br><span class="line">      *dst++ = value;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将src_起始的size个字节复制到dst_ */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">memcpy</span><span class="params">(<span class="type">void</span>* dst_, <span class="type">const</span> <span class="type">void</span>* src_, <span class="type">uint32_t</span> size)</span> {</span><br><span class="line">   ASSERT(dst_ != <span class="literal">NULL</span> &amp;&amp; src_ != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="type">uint8_t</span>* dst = dst_;</span><br><span class="line">   <span class="type">const</span> <span class="type">uint8_t</span>* src = src_;</span><br><span class="line">   <span class="keyword">while</span> (size-- &gt; <span class="number">0</span>)</span><br><span class="line">      *dst++ = *src++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 连续比较以地址a_和地址b_开头的size个字节,若相等则返回0,若a_大于b_返回+1,否则返回-1 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">memcmp</span><span class="params">(<span class="type">const</span> <span class="type">void</span>* a_, <span class="type">const</span> <span class="type">void</span>* b_, <span class="type">uint32_t</span> size)</span> {</span><br><span class="line">   <span class="type">const</span> <span class="type">char</span>* a = a_;</span><br><span class="line">   <span class="type">const</span> <span class="type">char</span>* b = b_;</span><br><span class="line">   ASSERT(a != <span class="literal">NULL</span> || b != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">while</span> (size-- &gt; <span class="number">0</span>) {</span><br><span class="line">      <span class="keyword">if</span>(*a != *b) {</span><br><span class="line"> <span class="keyword">return</span> *a &gt; *b ? <span class="number">1</span> : <span class="number">-1</span>; </span><br><span class="line">      }</span><br><span class="line">      a++;</span><br><span class="line">      b++;</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将字符串从src_复制到dst_ */</span></span><br><span class="line"><span class="type">char</span>* <span class="title function_">strcpy</span><span class="params">(<span class="type">char</span>* dst_, <span class="type">const</span> <span class="type">char</span>* src_)</span> {</span><br><span class="line">   ASSERT(dst_ != <span class="literal">NULL</span> &amp;&amp; src_ != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="type">char</span>* r = dst_;       <span class="comment">// 用来返回目的字符串起始地址</span></span><br><span class="line">   <span class="keyword">while</span>((*dst_++ = *src_++));</span><br><span class="line">   <span class="keyword">return</span> r;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回字符串长度 */</span></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">strlen</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str)</span> {</span><br><span class="line">   ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="type">const</span> <span class="type">char</span>* p = str;</span><br><span class="line">   <span class="keyword">while</span>(*p++);</span><br><span class="line">   <span class="keyword">return</span> (p - str - <span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */</span></span><br><span class="line"><span class="type">int8_t</span> <span class="title function_">strcmp</span> <span class="params">(<span class="type">const</span> <span class="type">char</span>* a, <span class="type">const</span> <span class="type">char</span>* b)</span> {</span><br><span class="line">   ASSERT(a != <span class="literal">NULL</span> &amp;&amp; b != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">while</span> (*a != <span class="number">0</span> &amp;&amp; *a == *b) {</span><br><span class="line">      a++;</span><br><span class="line">      b++;</span><br><span class="line">   }</span><br><span class="line"><span class="comment">/* 如果*a小于*b就返回-1,否则就属于*a大于等于*b的情况。在后面的布尔表达式"*a &gt; *b"中,</span></span><br><span class="line"><span class="comment"> * 若*a大于*b,表达式就等于1,否则就表达式不成立,也就是布尔值为0,恰恰表示*a等于*b */</span></span><br><span class="line">   <span class="keyword">return</span> *a &lt; *b ? <span class="number">-1</span> : *a &gt; *b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */</span></span><br><span class="line"><span class="type">char</span>* <span class="title function_">strchr</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str, <span class="type">const</span> <span class="type">uint8_t</span> ch)</span> {</span><br><span class="line">   ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">while</span> (*str != <span class="number">0</span>) {</span><br><span class="line">      <span class="keyword">if</span> (*str == ch) {</span><br><span class="line"> <span class="keyword">return</span> (<span class="type">char</span>*)str;    <span class="comment">// 需要强制转化成和返回值类型一样,否则编译器会报const属性丢失,下同.</span></span><br><span class="line">      }</span><br><span class="line">      str++;</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从后往前查找字符串str中首次出现字符ch的地址(不是下标,是地址) */</span></span><br><span class="line"><span class="type">char</span>* <span class="title function_">strrchr</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str, <span class="type">const</span> <span class="type">uint8_t</span> ch)</span> {</span><br><span class="line">   ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="type">const</span> <span class="type">char</span>* last_char = <span class="literal">NULL</span>;</span><br><span class="line">   <span class="comment">/* 从头到尾遍历一次,若存在ch字符,last_char总是该字符最后一次出现在串中的地址(不是下标,是地址)*/</span></span><br><span class="line">   <span class="keyword">while</span> (*str != <span class="number">0</span>) {</span><br><span class="line">      <span class="keyword">if</span> (*str == ch) {</span><br><span class="line"> last_char = str;</span><br><span class="line">      }</span><br><span class="line">      str++;</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> (<span class="type">char</span>*)last_char;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将字符串src_拼接到dst_后,将回拼接的串地址 */</span></span><br><span class="line"><span class="type">char</span>* <span class="title function_">strcat</span><span class="params">(<span class="type">char</span>* dst_, <span class="type">const</span> <span class="type">char</span>* src_)</span> {</span><br><span class="line">   ASSERT(dst_ != <span class="literal">NULL</span> &amp;&amp; src_ != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="type">char</span>* str = dst_;</span><br><span class="line">   <span class="keyword">while</span> (*str++);</span><br><span class="line">   --str;      <span class="comment">// 别看错了，--str是独立的一句，并不是while的循环体</span></span><br><span class="line">   <span class="keyword">while</span>((*str++ = *src_++)); <span class="comment">// 当*str被赋值为0时,此时表达式不成立,正好添加了字符串结尾的0.</span></span><br><span class="line">   <span class="keyword">return</span> dst_;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在字符串str中查找指定字符ch出现的次数 */</span></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">strchrs</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str, <span class="type">uint8_t</span> ch)</span> {</span><br><span class="line">   ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="type">uint32_t</span> ch_cnt = <span class="number">0</span>;</span><br><span class="line">   <span class="type">const</span> <span class="type">char</span>* p = str;</span><br><span class="line">   <span class="keyword">while</span>(*p != <span class="number">0</span>) {</span><br><span class="line">      <span class="keyword">if</span> (*p == ch) {</span><br><span class="line"> ch_cnt++;</span><br><span class="line">      }</span><br><span class="line">      p++;</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> ch_cnt;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>同时这里要添加 NULL 的定义,可以先暂时添加到 stdint.h文件中:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> NULL ((void*)0) </span></span><br></pre></td></tr></table></figure></div><p>编译写入运行后大家就可以在主函数中调用这些函数来测试，比如 <code>strlen("1234")</code>,然后用<code>put_int</code>输出验证等等</p><hr><h2 id="D-位图-bitmap-及其函数的实现"><a href="#D-位图-bitmap-及其函数的实现" class="headerlink" title="D 位图 bitmap 及其函数的实现"></a>D 位图 bitmap 及其函数的实现</h2><p>位图，也就是 bitmap，广泛用于资源管理，是一种管理资源的方式、手段。“资源”包括很多，比如内存或硬盘，对于此类大容量资源的管理一般都会采用位图的方式</p><blockquote><p>位是指 bit，即字节中的位，1 字节中有 8 个位。图是指 map，map 这个词在很久之前就介绍过啦，地图本质上就是映射的意思，映射，即对应关系。综合起来，位图就是用字节中的 1 位来映射其他单位大小的资源，按位与资源之间是一对一的对应关系</p></blockquote><p>总结一下，<strong>位图相当于一组资源的映射。位图中的每一位和被管理的单位资源都是一对一的关系，故位图主要用于管理容量较大的资源</strong></p><p>这里还是通过实际的位图代码来理解,这里在<code>mouse/lib/kernel/</code>下创建 <code>bitmap.c</code> <code>bitmap.h</code>文件:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/bitmap.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_KERNEL_BITMAP_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_KERNEL_BITMAP_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BITMAP_MASK 1</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line">    <span class="type">uint32_t</span> btmp_bytes_len;        <span class="comment">//在遍历位图时，整体上以字节为单位，细节上是以位位单位，所以此处位图的指针必须是单字节</span></span><br><span class="line">    <span class="type">uint8_t</span>* bits;                  <span class="comment">//一个字节</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_init</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">bitmap_scan_test</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp,<span class="type">uint32_t</span> bit_idx)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">bitmap_scan</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp,<span class="type">uint32_t</span> cnt)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_set</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp,<span class="type">uint32_t</span> bit_idx,<span class="type">int8_t</span> value)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/bitmap.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化位图 btmp</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_init</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp)</span></span><br><span class="line">{</span><br><span class="line">    <span class="built_in">memset</span>(btmp-&gt;bits,<span class="number">0</span>,btmp-&gt;btmp_bytes_len);  <span class="comment">//清零</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//判断位图的第bit_idx位是否为1，如果为1，返回true，否则返回false</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">bitmap_scan_test</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp,<span class="type">uint32_t</span> bit_idx)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span> byte_idx = bit_idx/<span class="number">8</span>;  <span class="comment">//向下取整</span></span><br><span class="line">    <span class="type">uint32_t</span> bit_odd = bit_idx%<span class="number">8</span>;   <span class="comment">//取余获得索引数组的位</span></span><br><span class="line">    <span class="keyword">return</span> (btmp-&gt;bits[byte_idx]) &amp; (BITMAP_MASK &lt;&lt; bit_odd);   <span class="comment">//掩码判断具体的位是否为1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//在位图中连续申请cnt个位，成功返回起始下表，失败返回-1</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">bitmap_scan</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp,<span class="type">uint32_t</span> cnt)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span> idx_byte = <span class="number">0</span>;      <span class="comment">//用于记录空闲位所在字节</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//先逐字节比较，蛮力</span></span><br><span class="line">    <span class="keyword">while</span>((<span class="number">0xff</span> == btmp-&gt;bits[idx_byte]) &amp;&amp; (idx_byte &lt; btmp-&gt;btmp_bytes_len))</span><br><span class="line">    {</span><br><span class="line">        idx_byte++; <span class="comment">//如果为0xff，表示该字节已经没有空闲位置，向下一个字节查找</span></span><br><span class="line">    }</span><br><span class="line">    ASSERT(idx_byte &lt; btmp-&gt;btmp_bytes_len);</span><br><span class="line">    <span class="keyword">if</span>(idx_byte == btmp-&gt;btmp_bytes_len)        <span class="comment">//内存池找不到可用空间</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">//若找到了空闲位，逐位比较，然后返回索引:</span></span><br><span class="line">    <span class="type">int</span> idx_bit = <span class="number">0</span>;        </span><br><span class="line">    <span class="keyword">while</span>((<span class="type">uint8_t</span>)(BITMAP_MASK &lt;&lt; idx_bit) &amp; btmp-&gt;bits[idx_byte])</span><br><span class="line">    {</span><br><span class="line">        idx_bit++;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> bit_idx_start = idx_byte*<span class="number">8</span> + idx_bit;       <span class="comment">//空闲位图内的下标</span></span><br><span class="line">    <span class="keyword">if</span>(cnt == <span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span> bit_idx_start;   <span class="comment">//刚好一个</span></span><br><span class="line">    }</span><br><span class="line">    <span class="type">uint32_t</span> bit_left = (btmp-&gt;btmp_bytes_len*<span class="number">8</span> - bit_idx_start);<span class="comment">//获得剩余可判断的位</span></span><br><span class="line">    <span class="type">uint32_t</span> next_bit = bit_idx_start+<span class="number">1</span>;</span><br><span class="line">    <span class="type">uint32_t</span> count = <span class="number">1</span>;     <span class="comment">//记录空闲位的个数</span></span><br><span class="line">    bit_idx_start = <span class="number">-1</span>;     <span class="comment">//先改成-1，如果没找到直接返回</span></span><br><span class="line">    <span class="keyword">while</span>(bit_left-- &gt; <span class="number">0</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">if</span>(!(bitmap_scan_test(btmp,next_bit)))</span><br><span class="line">        {</span><br><span class="line">            <span class="comment">//若next_bit == 0</span></span><br><span class="line">            count++;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        {</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span>(count == cnt)    <span class="comment">//找到了连续的cnt个空位</span></span><br><span class="line">        {</span><br><span class="line">            bit_idx_start = next_bit - cnt +<span class="number">1</span>;   <span class="comment">//计算起始位</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        }</span><br><span class="line">        next_bit++; <span class="comment">//查找下一个位</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> bit_idx_start;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置位图btmp的bit_idx位为value</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_set</span><span class="params">(<span class="keyword">struct</span> bitmap* btmp,<span class="type">uint32_t</span> bit_idx,<span class="type">int8_t</span> value)</span></span><br><span class="line">{</span><br><span class="line">    ASSERT((value == <span class="number">0</span>) || (value ==<span class="number">1</span>));</span><br><span class="line">    <span class="type">uint32_t</span> byte_idx = bit_idx/<span class="number">8</span>;  <span class="comment">//向下取整</span></span><br><span class="line">    <span class="type">uint32_t</span> bit_odd = bit_idx %<span class="number">8</span>;  <span class="comment">//获得余数，即索引数组的位</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//一般会使用0x1这样的数字对字节进行位操作，将1任意移动后再取反，或者取反再移位</span></span><br><span class="line">    <span class="keyword">if</span>(value) <span class="comment">//value ==1</span></span><br><span class="line">    {</span><br><span class="line">        btmp-&gt;bits[byte_idx] |= (BITMAP_MASK &lt;&lt;bit_odd);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        btmp-&gt;bits[byte_idx] &amp;= ~(BITMAP_MASK &lt;&lt;bit_odd);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>注意还要添加Makefile里面的c资源<code>bitmap.c</code>,下面就开始初入内存管理吧:</p><hr><h2 id="E-内存管理系统"><a href="#E-内存管理系统" class="headerlink" title="E 内存管理系统"></a>E 内存管理系统</h2><blockquote><p>用户程序所占用的内存空间是由操作系统分配的，内存是如何分配的并且该给用户进程分配多少字节呢？这就是咱们要解决的问题。<br>所以，从现在起，咱们要循序渐进地实现内存管理系统，一直到函数 malloc 和 free 的完成</p></blockquote><h3 id="E-1-内存池规划"><a href="#E-1-内存池规划" class="headerlink" title="E.1 内存池规划"></a>E.1 内存池规划</h3><ul><li><strong>物理内存池</strong></li></ul><p>这里我们首先来规划物理内存池,这里将它分成两部分，一部分称为<strong>用户物理内存池</strong>，一部分称为<strong>内核物理内存池</strong>(只给操作系统使用)</p><p>同时，每次分配内存也是按单位大小来获取，这个单位的大小是 4KB，也称为页，所以内存池管理的是一个个为4KB的内存块，从内存池中获取的内存大小至少为 4KB 或者为 4KB 的倍数(当然，以后会实现更颗粒度的内存管理)</p><ul><li><strong>虚拟内存池</strong></li></ul><p>前情回顾:</p><blockquote><p>在分页机制下程序中的地址都是虚拟地址，虚拟地址的范围取决于地址总线的宽度，咱们是在 32 位环境下，所以虚拟地址空间为 4GB。除了地址空间比较大以外，分页机制的另一个好处是每个任务都有自己的 4GB 虚拟地址空间，也就是各程序中的虚拟地址不会与其他程序冲突，都可以为相同的虚拟地址，不仅用户进程是这样，内核也是;同时虚拟地址池的单位也是4KB</p></blockquote><p>同时这里也是分为<strong>用户虚拟地址池</strong>，<strong>内核虚拟地址池</strong></p><p>下面还是直接动手实践: 在<code>/mouse/kernel</code>目录下建立 <code>memory.h</code> <code>memory.c</code>,有关内存管理的代码都放在这里,同时记得要在Makefile 里面添加东西:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __KERNEL_MEMORY_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __KERNEL_MEMORY_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//虚拟地址池，用于虚拟地址管理</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">vaddr_bitmap</span>;</span> <span class="comment">//虚拟地址的位图</span></span><br><span class="line">    <span class="type">uint32_t</span> vaddr_start;       <span class="comment">//虚拟地址的起始地址</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">pool</span> <span class="title">kernel_pool</span>, <span class="title">user_pool</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mem_init</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_SIZE 4096</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/***************  位图地址 ********************</span></span><br><span class="line"><span class="comment"> * 因为0xc009f000是内核主线程栈顶，0xc009e000是内核主线程的pcb.</span></span><br><span class="line"><span class="comment"> * 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000,</span></span><br><span class="line"><span class="comment"> * 这样本系统最大支持4个页框的位图,即512M */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MEM_BITMAP_BASE 0xc009a000</span></span><br><span class="line"><span class="comment">/*************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存,使虚拟地址在逻辑上连续 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> K_HEAP_START 0xc0100000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存池结构,生成两个实例用于管理内核内存池和用户内存池 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> {</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">pool_bitmap</span>;</span> <span class="comment">// 本内存池用到的位图结构,用于管理物理内存</span></span><br><span class="line">   <span class="type">uint32_t</span> phy_addr_start; <span class="comment">// 本内存池所管理物理内存的起始地址</span></span><br><span class="line">   <span class="type">uint32_t</span> pool_size; <span class="comment">// 本内存池字节容量</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> <span class="title">kernel_pool</span>, <span class="title">user_pool</span>;</span>      <span class="comment">// 生成内核内存池和用户内存池</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">kernel_vaddr</span>;</span> <span class="comment">// 此结构是用来给内核分配虚拟地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化内存池 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">mem_pool_init</span><span class="params">(<span class="type">uint32_t</span> all_mem)</span> {</span><br><span class="line">   put_str(<span class="string">"   mem_pool_init start\n"</span>);</span><br><span class="line">   <span class="type">uint32_t</span> page_table_size = PG_SIZE * <span class="number">256</span>;  <span class="comment">// 页表大小= 1页的页目录表+第0和第768个页目录项指向同一个页表+</span></span><br><span class="line">                                                  <span class="comment">// 第769~1022个页目录项共指向254个页表,共256个页框</span></span><br><span class="line">   <span class="type">uint32_t</span> used_mem = page_table_size + <span class="number">0x100000</span>;  <span class="comment">// 0x100000为低端1M内存</span></span><br><span class="line">   <span class="type">uint32_t</span> free_mem = all_mem - used_mem;</span><br><span class="line">   <span class="type">uint16_t</span> all_free_pages = free_mem / PG_SIZE;    <span class="comment">// 1页为4k,不管总内存是不是4k的倍数,</span></span><br><span class="line">                    <span class="comment">// 对于以页为单位的内存分配策略，不足1页的内存不用考虑了。</span></span><br><span class="line">   <span class="type">uint16_t</span> kernel_free_pages = all_free_pages / <span class="number">2</span>;</span><br><span class="line">   <span class="type">uint16_t</span> user_free_pages = all_free_pages - kernel_free_pages;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 为简化位图操作，余数不处理，坏处是这样做会丢内存。</span></span><br><span class="line"><span class="comment">好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存*/</span></span><br><span class="line">   <span class="type">uint32_t</span> kbm_length = kernel_free_pages / <span class="number">8</span>;  <span class="comment">// Kernel BitMap的长度,位图中的一位表示一页,以字节为单位</span></span><br><span class="line">   <span class="type">uint32_t</span> ubm_length = user_free_pages / <span class="number">8</span>;  <span class="comment">// User BitMap的长度.</span></span><br><span class="line"></span><br><span class="line">   <span class="type">uint32_t</span> kp_start = used_mem;  <span class="comment">// Kernel Pool start,内核内存池的起始地址</span></span><br><span class="line">   <span class="type">uint32_t</span> up_start = kp_start + kernel_free_pages * PG_SIZE;  <span class="comment">// User Pool start,用户内存池的起始地址</span></span><br><span class="line"></span><br><span class="line">   kernel_pool.phy_addr_start = kp_start;</span><br><span class="line">   user_pool.phy_addr_start   = up_start;</span><br><span class="line"></span><br><span class="line">   kernel_pool.pool_size = kernel_free_pages * PG_SIZE;</span><br><span class="line">   user_pool.pool_size = user_free_pages * PG_SIZE;</span><br><span class="line"></span><br><span class="line">   kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;</span><br><span class="line">   user_pool.pool_bitmap.btmp_bytes_len  = ubm_length;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*********    内核内存池和用户内存池位图   ***********</span></span><br><span class="line"><span class="comment"> *   位图是全局的数据，长度不固定。</span></span><br><span class="line"><span class="comment"> *   全局或静态的数组需要在编译时知道其长度，</span></span><br><span class="line"><span class="comment"> *   而我们需要根据总内存大小算出需要多少字节。</span></span><br><span class="line"><span class="comment"> *   所以改为指定一块内存来生成位图.</span></span><br><span class="line"><span class="comment"> *   ************************************************/</span></span><br><span class="line"><span class="comment">// 内核使用的最高地址是0xc009f000,这是主线程的栈地址.(内核的大小预计为70K左右)</span></span><br><span class="line"><span class="comment">// 32M内存占用的位图是2k.内核内存池的位图先定在MEM_BITMAP_BASE(0xc009a000)处.</span></span><br><span class="line">   kernel_pool.pool_bitmap.bits = (<span class="type">void</span>*)MEM_BITMAP_BASE;</span><br><span class="line">       </span><br><span class="line"><span class="comment">/* 用户内存池的位图紧跟在内核内存池位图之后 */</span></span><br><span class="line">   user_pool.pool_bitmap.bits = (<span class="type">void</span>*)(MEM_BITMAP_BASE + kbm_length);</span><br><span class="line">   <span class="comment">/******************** 输出内存池信息 **********************/</span></span><br><span class="line">   put_str(<span class="string">"      kernel_pool_bitmap_start:"</span>);</span><br><span class="line">   put_int((<span class="type">int</span>)kernel_pool.pool_bitmap.bits);</span><br><span class="line">   put_str(<span class="string">" kernel_pool_phy_addr_start:"</span>);</span><br><span class="line">   put_int(kernel_pool.phy_addr_start);</span><br><span class="line">   put_str(<span class="string">"\n"</span>);</span><br><span class="line">   put_str(<span class="string">"      user_pool_bitmap_start:"</span>);</span><br><span class="line">   put_int((<span class="type">int</span>)user_pool.pool_bitmap.bits);</span><br><span class="line">   put_str(<span class="string">" user_pool_phy_addr_start:"</span>);</span><br><span class="line">   put_int(user_pool.phy_addr_start);</span><br><span class="line">   put_str(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将位图置0*/</span></span><br><span class="line">   bitmap_init(&amp;kernel_pool.pool_bitmap);</span><br><span class="line">   bitmap_init(&amp;user_pool.pool_bitmap);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 下面初始化内核虚拟地址的位图,按实际物理内存大小生成数组。*/</span></span><br><span class="line">   kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;      <span class="comment">// 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">/* 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之外*/</span></span><br><span class="line">   kernel_vaddr.vaddr_bitmap.bits = (<span class="type">void</span>*)(MEM_BITMAP_BASE + kbm_length + ubm_length);</span><br><span class="line"></span><br><span class="line">   kernel_vaddr.vaddr_start = K_HEAP_START;</span><br><span class="line">   bitmap_init(&amp;kernel_vaddr.vaddr_bitmap);</span><br><span class="line">   put_str(<span class="string">"   mem_pool_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存管理部分初始化入口 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mem_init</span><span class="params">()</span> {</span><br><span class="line">   put_str(<span class="string">"mem_init start\n"</span>);</span><br><span class="line">   <span class="type">uint32_t</span> mem_bytes_total = (*(<span class="type">uint32_t</span>*)(<span class="number">0xb00</span>));</span><br><span class="line">   mem_pool_init(mem_bytes_total);  <span class="comment">// 初始化内存池</span></span><br><span class="line">   put_str(<span class="string">"mem_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最后将<code>mem_init()</code>函数添加到 <code>init.c</code>中即可,同时如果编译报错<code>bool未定义</code> 的话可以在<code>stdint.h</code>中添加定义如下：</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> BOOL int</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> bool int</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> true 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> false 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TRUE 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> FALSE 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> NULL ((void*)0) </span></span><br></pre></td></tr></table></figure></div><p>然后<code>make</code> <code>make img</code> ，运行就可以看到结果大致如下:</p><blockquote><p>Mosuel am kernel!<br>init all<br>idt init start<br>idt desc init done<br>pic init done<br>idt init done<br>timer init start<br>timer init donemem init startmem pool init start<br>kernel pool bitmap _start:c009A000 kernel pool &gt;phy_addr_start:200000user pool bitmap<br>start:c009A1E0 user pool phy_addr start:1100000mem &gt;pool init donemem init done</p></blockquote><p>大家可以仔细看看代码中的注释，下面就是如何来分配内存了</p><hr><h3 id="E-2-分配页内存"><a href="#E-2-分配页内存" class="headerlink" title="E.2 分配页内存"></a>E.2 分配页内存</h3><p>有了内存池之后，就是分配内存了，我们先来学习如何分配页内存(支持一次分配 n 个页的内存,即n*4096 字节)</p><p>这里分为了三步，也是我们等会要实现的</p><p>（1）首先处理高 10 位的 pde 索引，从而处理器得到页表物理地址。<br>（2）其次处理中间 10 位的 pte 索引，进而处理器得到普通物理页的物理地址。<br>（3）最后是把低 12 位作为普通物理页的页内偏移地址，此偏移地址加上物理页的物理地址，得到的地址之和便是最终的物理地址，处理器到此物理地址上进行读写操作。</p><p>下面还是修改之前的两个文件:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_SIZE 4096</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PDE_IDX(addr) ((addr &amp; 0xffc00000) &gt;&gt; 22)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PTE_IDX(addr) ((addr &amp; 0x003FF000) &gt;&gt; 12)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/***************  位图地址 ********************</span></span><br><span class="line"><span class="comment"> * 因为0xc009f000是内核主线程栈顶，0xc009e000是内核主线程的pcb.</span></span><br><span class="line"><span class="comment"> * 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000,</span></span><br><span class="line"><span class="comment"> * 这样本系统最大支持4个页框的位图,即512M */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MEM_BITMAP_BASE 0xc009a000</span></span><br><span class="line"><span class="comment">/*************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存,使虚拟地址在逻辑上连续 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> K_HEAP_START 0xc0100000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存池结构,生成两个实例用于管理内核内存池和用户内存池 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> {</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">pool_bitmap</span>;</span> <span class="comment">// 本内存池用到的位图结构,用于管理物理内存</span></span><br><span class="line">   <span class="type">uint32_t</span> phy_addr_start; <span class="comment">// 本内存池所管理物理内存的起始地址</span></span><br><span class="line">   <span class="type">uint32_t</span> pool_size; <span class="comment">// 本内存池字节容量</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> <span class="title">kernel_pool</span>, <span class="title">user_pool</span>;</span>      <span class="comment">// 生成内核内存池和用户内存池</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">kernel_vaddr</span>;</span> <span class="comment">// 此结构是用来给内核分配虚拟地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在 pf 表示的虚拟内存池中申请 pg_cnt 个虚拟页，</span></span><br><span class="line"><span class="comment"> * 成功则返回虚拟页的起始地址，失败则返回 NULL */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span>* <span class="title function_">vaddr_get</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="type">uint32_t</span> pg_cnt)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> vaddr_start = <span class="number">0</span>, bit_idx_start = <span class="number">-1</span>; </span><br><span class="line">    <span class="type">uint32_t</span> cnt = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">if</span> (pf == PF_KERNEL) </span><br><span class="line">    { </span><br><span class="line">        bit_idx_start = bitmap_scan(&amp;kernel_vaddr.vaddr_bitmap, pg_cnt); </span><br><span class="line">        <span class="keyword">if</span> (bit_idx_start == <span class="number">-1</span>) </span><br><span class="line">        { </span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NULL</span>; </span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">while</span>(cnt &lt; pg_cnt) </span><br><span class="line">        { </span><br><span class="line">            bitmap_set(&amp;kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, <span class="number">1</span>); </span><br><span class="line">        } </span><br><span class="line">        vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE; </span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        <span class="comment">// 用户内存池，将来实现用户进程再补充</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">void</span>*)vaddr_start; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 得到虚拟地址 vaddr 对应的 pte 指针*/</span> </span><br><span class="line"><span class="type">uint32_t</span>* <span class="title function_">pte_ptr</span><span class="params">(<span class="type">uint32_t</span> vaddr)</span> </span><br><span class="line">{ </span><br><span class="line"><span class="comment">/* 先访问到页表自己 + </span></span><br><span class="line"><span class="comment"> * 再用页目录项 pde（页目录内页表的索引）作为 pte 的索引访问到页表 + </span></span><br><span class="line"><span class="comment"> * 再用 pte 的索引作为页内偏移      */</span> </span><br><span class="line">    <span class="type">uint32_t</span>* pte = (<span class="type">uint32_t</span>*)(<span class="number">0xffc00000</span> + \</span><br><span class="line">                    ((vaddr &amp; <span class="number">0xffc00000</span>) &gt;&gt; <span class="number">10</span>) + \</span><br><span class="line">                    PTE_IDX(vaddr) * <span class="number">4</span>); </span><br><span class="line">    <span class="keyword">return</span> pte; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 得到虚拟地址 vaddr 对应的 pde 的指针 */</span> </span><br><span class="line"><span class="type">uint32_t</span>* <span class="title function_">pde_ptr</span><span class="params">(<span class="type">uint32_t</span> vaddr)</span> </span><br><span class="line">{ </span><br><span class="line"><span class="comment">/* 0xfffff 用来访问到页表本身所在的地址 */</span> </span><br><span class="line">    <span class="type">uint32_t</span>* pde = (<span class="type">uint32_t</span>*)((<span class="number">0xfffff000</span>) + PDE_IDX(vaddr) * <span class="number">4</span>); </span><br><span class="line">    <span class="keyword">return</span> pde; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在 m_pool 指向的物理内存池中分配 1 个物理页，</span></span><br><span class="line"><span class="comment">* 成功则返回页框的物理地址，失败则返回 NULL */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span>* <span class="title function_">palloc</span><span class="params">(<span class="keyword">struct</span> pool* m_pool)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/* 扫描或设置位图要保证原子操作 */</span> </span><br><span class="line">    <span class="type">int</span> bit_idx = bitmap_scan(&amp;m_pool-&gt;pool_bitmap, <span class="number">1</span>); <span class="comment">// 找一个物理页面</span></span><br><span class="line">    <span class="keyword">if</span> (bit_idx == <span class="number">-1</span> ) </span><br><span class="line">    { </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>; </span><br><span class="line">    } </span><br><span class="line">    bitmap_set(&amp;m_pool-&gt;pool_bitmap, bit_idx, <span class="number">1</span>); <span class="comment">// 将此位 bit_idx 置 1 </span></span><br><span class="line">    <span class="type">uint32_t</span> page_phyaddr = ((bit_idx * PG_SIZE) + m_pool-&gt;phy_addr_start); </span><br><span class="line">    <span class="keyword">return</span> (<span class="type">void</span>*)page_phyaddr; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 页表中添加虚拟地址_vaddr 与物理地址_page_phyaddr 的映射 */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">page_table_add</span><span class="params">(<span class="type">void</span>* _vaddr, <span class="type">void</span>* _page_phyaddr)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">uint32_t</span> vaddr = (<span class="type">uint32_t</span>)_vaddr, page_phyaddr = (<span class="type">uint32_t</span>)_page_phyaddr; </span><br><span class="line">    <span class="type">uint32_t</span>* pde = pde_ptr(vaddr); </span><br><span class="line">    <span class="type">uint32_t</span>* pte = pte_ptr(vaddr); </span><br><span class="line"><span class="comment">/************************ 注意 ************************* </span></span><br><span class="line"><span class="comment"> * 执行*pte，会访问到空的 pde。所以确保 pde 创建完成后才能执行*pte，</span></span><br><span class="line"><span class="comment"> * 否则会引发 page_fault。因此在*pde 为 0 时，</span></span><br><span class="line"><span class="comment"> *pte 只能出现在下面 else 语句块中的*pde 后面。</span></span><br><span class="line"><span class="comment"> ***********************************************************/</span> </span><br><span class="line"> <span class="comment">/* 先在页目录内判断目录项的 P 位，若为 1，则表示该表已存在 */</span> </span><br><span class="line">    <span class="keyword">if</span> (*pde &amp; <span class="number">0x00000001</span>) </span><br><span class="line">    { </span><br><span class="line">        <span class="comment">// 页目录项和页表项的第 0 位为 P，此处判断目录项是否存在</span></span><br><span class="line">        ASSERT(!(*pte &amp; <span class="number">0x00000001</span>)); </span><br><span class="line">        <span class="keyword">if</span> (!(*pte &amp; <span class="number">0x00000001</span>)) </span><br><span class="line">        { </span><br><span class="line">            <span class="comment">// 只要是创建页表，pte 就应该不存在，多判断一下放心</span></span><br><span class="line">            *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); </span><br><span class="line">            <span class="comment">// US=1,RW=1,P=1 </span></span><br><span class="line">        } </span><br><span class="line">        <span class="keyword">else</span> </span><br><span class="line">        {       <span class="comment">//目前应该不会执行到这，因为上面的 ASSERT 会先执行</span></span><br><span class="line">            PANIC(<span class="string">"pte repeat"</span>); </span><br><span class="line">            *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); </span><br><span class="line">            <span class="comment">// US=1,RW=1,P=1 </span></span><br><span class="line">        } </span><br><span class="line">    } </span><br><span class="line">    <span class="keyword">else</span> </span><br><span class="line">    { </span><br><span class="line">    <span class="comment">// 页目录项不存在，所以要先创建页目录再创建页表项</span></span><br><span class="line">    <span class="comment">/* 页表中用到的页框一律从内核空间分配 */</span> </span><br><span class="line">        <span class="type">uint32_t</span> pde_phyaddr = (<span class="type">uint32_t</span>)palloc(&amp;kernel_pool); </span><br><span class="line">        *pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1); </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 分配到的物理页地址 pde_phyaddr 对应的物理内存清 0，</span></span><br><span class="line"><span class="comment">    * 避免里面的陈旧数据变成了页表项，从而让页表混乱。</span></span><br><span class="line"><span class="comment">    * 访问到 pde 对应的物理地址，用 pte 取高 20 位便可。</span></span><br><span class="line"><span class="comment">    * 因为 pte 基于该 pde 对应的物理地址内再寻址，</span></span><br><span class="line"><span class="comment">    * 把低 12 位置 0 便是该 pde 对应的物理页的起始*/</span> </span><br><span class="line">        <span class="built_in">memset</span>((<span class="type">void</span>*)((<span class="type">int</span>)pte &amp; <span class="number">0xfffff000</span>), <span class="number">0</span>, PG_SIZE); </span><br><span class="line">        *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); </span><br><span class="line">        <span class="comment">// US=1,RW=1,P=1 </span></span><br><span class="line">    } </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 分配 pg_cnt 个页空间，成功则返回起始虚拟地址，失败时返回 NULL */</span> </span><br><span class="line"><span class="type">void</span>* <span class="title function_">malloc_page</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="type">uint32_t</span> pg_cnt)</span> </span><br><span class="line">{ </span><br><span class="line">    ASSERT(pg_cnt &gt; <span class="number">0</span> &amp;&amp; pg_cnt &lt; <span class="number">3840</span>); </span><br><span class="line">    <span class="comment">/*********** malloc_page 的原理是三个动作的合成: *********** </span></span><br><span class="line"><span class="comment">    1 通过 vaddr_get 在虚拟内存池中申请虚拟地址</span></span><br><span class="line"><span class="comment">    2 通过 palloc 在物理内存池中申请物理页</span></span><br><span class="line"><span class="comment">    3 通过 page_table_add 将以上得到的虚拟地址和物理地址在页表中完成映射</span></span><br><span class="line"><span class="comment">    ********************************************************************/</span> </span><br><span class="line">    <span class="type">void</span>* vaddr_start = vaddr_get(pf, pg_cnt); </span><br><span class="line">    <span class="keyword">if</span> (vaddr_start == <span class="literal">NULL</span>) </span><br><span class="line">    { </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>; </span><br><span class="line">    }   </span><br><span class="line">    <span class="type">uint32_t</span> vaddr = (<span class="type">uint32_t</span>)vaddr_start, cnt = pg_cnt; </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span> =</span> pf &amp; PF_KERNEL ? &amp;kernel_pool : &amp;user_pool; </span><br><span class="line">    <span class="comment">/* 因为虚拟地址是连续的，但物理地址可以是不连续的，所以逐个做映射*/</span> </span><br><span class="line">    <span class="keyword">while</span> (cnt-- &gt; <span class="number">0</span>) </span><br><span class="line">    { </span><br><span class="line">        <span class="type">void</span>* page_phyaddr = palloc(mem_pool); </span><br><span class="line">        <span class="keyword">if</span> (page_phyaddr == <span class="literal">NULL</span>) </span><br><span class="line">        {   <span class="comment">//失败时要将曾经已申请的虚拟地址和</span></span><br><span class="line">            <span class="comment">//物理页全部回滚，在将来完成内存回收时再补充</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NULL</span>; </span><br><span class="line">        } </span><br><span class="line">        page_table_add((<span class="type">void</span>*)vaddr, page_phyaddr); <span class="comment">// 在页表中做映射</span></span><br><span class="line">        vaddr += PG_SIZE; <span class="comment">// 下一个虚拟页</span></span><br><span class="line">    } </span><br><span class="line">        <span class="keyword">return</span> vaddr_start; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从内核物理内存池中申请 1 页内存，</span></span><br><span class="line"><span class="comment">    成功则返回其虚拟地址，失败则返回 NULL */</span> </span><br><span class="line"><span class="type">void</span>* <span class="title function_">get_kernel_pages</span><span class="params">(<span class="type">uint32_t</span> pg_cnt)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">void</span>* vaddr = malloc_page(PF_KERNEL, pg_cnt); </span><br><span class="line">    <span class="keyword">if</span> (vaddr != <span class="literal">NULL</span>) </span><br><span class="line">    {   <span class="comment">// 若分配的地址不为空，将页框清 0 后返回</span></span><br><span class="line">        <span class="built_in">memset</span>(vaddr, <span class="number">0</span>, pg_cnt * PG_SIZE); </span><br><span class="line">    } </span><br><span class="line">    <span class="keyword">return</span> vaddr; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化内存池 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">mem_pool_init</span><span class="params">(<span class="type">uint32_t</span> all_mem)</span> </span><br><span class="line">{</span><br><span class="line">   put_str(<span class="string">"   mem_pool_init start\n"</span>);</span><br><span class="line">   <span class="type">uint32_t</span> page_table_size = PG_SIZE * <span class="number">256</span>;  <span class="comment">// 页表大小= 1页的页目录表+第0和第768个页目录项指向同一个页表+</span></span><br><span class="line">                                                  <span class="comment">// 第769~1022个页目录项共指向254个页表,共256个页框</span></span><br><span class="line">   <span class="type">uint32_t</span> used_mem = page_table_size + <span class="number">0x100000</span>;  <span class="comment">// 0x100000为低端1M内存</span></span><br><span class="line">   <span class="type">uint32_t</span> free_mem = all_mem - used_mem;</span><br><span class="line">   <span class="type">uint16_t</span> all_free_pages = free_mem / PG_SIZE;    <span class="comment">// 1页为4k,不管总内存是不是4k的倍数,</span></span><br><span class="line">                    <span class="comment">// 对于以页为单位的内存分配策略，不足1页的内存不用考虑了。</span></span><br><span class="line">   <span class="type">uint16_t</span> kernel_free_pages = all_free_pages / <span class="number">2</span>;</span><br><span class="line">   <span class="type">uint16_t</span> user_free_pages = all_free_pages - kernel_free_pages;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 为简化位图操作，余数不处理，坏处是这样做会丢内存。</span></span><br><span class="line"><span class="comment">好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存*/</span></span><br><span class="line">   <span class="type">uint32_t</span> kbm_length = kernel_free_pages / <span class="number">8</span>;  <span class="comment">// Kernel BitMap的长度,位图中的一位表示一页,以字节为单位</span></span><br><span class="line">   <span class="type">uint32_t</span> ubm_length = user_free_pages / <span class="number">8</span>;  <span class="comment">// User BitMap的长度.</span></span><br><span class="line"></span><br><span class="line">   <span class="type">uint32_t</span> kp_start = used_mem;  <span class="comment">// Kernel Pool start,内核内存池的起始地址</span></span><br><span class="line">   <span class="type">uint32_t</span> up_start = kp_start + kernel_free_pages * PG_SIZE;  <span class="comment">// User Pool start,用户内存池的起始地址</span></span><br><span class="line"></span><br><span class="line">   kernel_pool.phy_addr_start = kp_start;</span><br><span class="line">   user_pool.phy_addr_start   = up_start;</span><br><span class="line"></span><br><span class="line">   kernel_pool.pool_size = kernel_free_pages * PG_SIZE;</span><br><span class="line">   user_pool.pool_size = user_free_pages * PG_SIZE;</span><br><span class="line"></span><br><span class="line">   kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;</span><br><span class="line">   user_pool.pool_bitmap.btmp_bytes_len  = ubm_length;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*********    内核内存池和用户内存池位图   ***********</span></span><br><span class="line"><span class="comment"> *   位图是全局的数据，长度不固定。</span></span><br><span class="line"><span class="comment"> *   全局或静态的数组需要在编译时知道其长度，</span></span><br><span class="line"><span class="comment"> *   而我们需要根据总内存大小算出需要多少字节。</span></span><br><span class="line"><span class="comment"> *   所以改为指定一块内存来生成位图.</span></span><br><span class="line"><span class="comment"> *   ************************************************/</span></span><br><span class="line"><span class="comment">// 内核使用的最高地址是0xc009f000,这是主线程的栈地址.(内核的大小预计为70K左右)</span></span><br><span class="line"><span class="comment">// 32M内存占用的位图是2k.内核内存池的位图先定在MEM_BITMAP_BASE(0xc009a000)处.</span></span><br><span class="line">   kernel_pool.pool_bitmap.bits = (<span class="type">void</span>*)MEM_BITMAP_BASE;</span><br><span class="line">       </span><br><span class="line"><span class="comment">/* 用户内存池的位图紧跟在内核内存池位图之后 */</span></span><br><span class="line">   user_pool.pool_bitmap.bits = (<span class="type">void</span>*)(MEM_BITMAP_BASE + kbm_length);</span><br><span class="line">   <span class="comment">/******************** 输出内存池信息 **********************/</span></span><br><span class="line">   put_str(<span class="string">"      kernel_pool_bitmap_start:"</span>);</span><br><span class="line">   put_int((<span class="type">int</span>)kernel_pool.pool_bitmap.bits);</span><br><span class="line">   put_str(<span class="string">" kernel_pool_phy_addr_start:"</span>);</span><br><span class="line">   put_int(kernel_pool.phy_addr_start);</span><br><span class="line">   put_str(<span class="string">"\n"</span>);</span><br><span class="line">   put_str(<span class="string">"      user_pool_bitmap_start:"</span>);</span><br><span class="line">   put_int((<span class="type">int</span>)user_pool.pool_bitmap.bits);</span><br><span class="line">   put_str(<span class="string">" user_pool_phy_addr_start:"</span>);</span><br><span class="line">   put_int(user_pool.phy_addr_start);</span><br><span class="line">   put_str(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将位图置0*/</span></span><br><span class="line">   bitmap_init(&amp;kernel_pool.pool_bitmap);</span><br><span class="line">   bitmap_init(&amp;user_pool.pool_bitmap);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 下面初始化内核虚拟地址的位图,按实际物理内存大小生成数组。*/</span></span><br><span class="line">   kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;      <span class="comment">// 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">/* 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之外*/</span></span><br><span class="line">   kernel_vaddr.vaddr_bitmap.bits = (<span class="type">void</span>*)(MEM_BITMAP_BASE + kbm_length + ubm_length);</span><br><span class="line"></span><br><span class="line">   kernel_vaddr.vaddr_start = K_HEAP_START;</span><br><span class="line">   bitmap_init(&amp;kernel_vaddr.vaddr_bitmap);</span><br><span class="line">   put_str(<span class="string">"   mem_pool_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存管理部分初始化入口 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mem_init</span><span class="params">()</span> </span><br><span class="line">{</span><br><span class="line">   put_str(<span class="string">"mem_init start\n"</span>);</span><br><span class="line">   <span class="type">uint32_t</span> mem_bytes_total = (*(<span class="type">uint32_t</span>*)(<span class="number">0xb00</span>));</span><br><span class="line">   mem_pool_init(mem_bytes_total);  <span class="comment">// 初始化内存池</span></span><br><span class="line">   put_str(<span class="string">"mem_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/memory.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __KERNEL_MEMORY_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __KERNEL_MEMORY_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"bitmap.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">pool_flags</span>{</span></span><br><span class="line">    PF_KERNEL = <span class="number">1</span>,      <span class="comment">//内核内存池</span></span><br><span class="line">    PF_USER = <span class="number">2</span>         <span class="comment">//用户内存池</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_P_1 1 <span class="comment">// 页表项或页目录项存在属性位</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_P_0 0 <span class="comment">// 页表项或页目录项存在属性位</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_RW_R 0 <span class="comment">// R/W 属性位值，读/执行</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_RW_W 2 <span class="comment">// R/W 属性位值，读/写/执行</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_US_S 0 <span class="comment">// U/S 属性位值，系统级</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_US_U 4 <span class="comment">// U/S 属性位值，用户级</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//虚拟地址池，用于虚拟地址管理</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span>{</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">vaddr_bitmap</span>;</span> <span class="comment">//虚拟地址的位图</span></span><br><span class="line">    <span class="type">uint32_t</span> vaddr_start;       <span class="comment">//虚拟地址的起始地址</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">pool</span> <span class="title">kernel_pool</span>, <span class="title">user_pool</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mem_init</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span>* <span class="title function_">get_kernel_pages</span><span class="params">(<span class="type">uint32_t</span> pg_cnt)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>下面说一下大概流程:</p><p>首先在mem_init 函数中通过读取<code>0xb00</code>地址处的内容，来获取总内存大小，这个是之前我们写的loader.S文件中自动获取的，然后调用mem_pool_init进行实际的内存池初始化;</p><p>实际的内存管理主要分为几个部分:</p><ol><li>内存池的初始化(主要包括先计算实际页表+低端1MB使用的空间，然后得到剩余的可用内存),同时也要给位图(用来管理这些位图)单独分配一块内存(0xc009a00),从而可以规划内核物理内存池，用户物理内存池的，虚拟内存池等的起始地址</li><li>然后是虚拟地址分配的函数:这里分为:先用位图扫描算法查找空闲的虚拟页，然后标记已经使用，最后返回虚拟地址</li><li>物理页的分配函数: 同样是扫描物理页的位图，然后计算对应的物理地址并返回</li><li>最重要的就是建立虚拟地址到物理地址的映射，并且当虚拟地址对应的物理地址对应的页目录项不存在的时候，也要分配一个物理页作为新的页表</li></ol><p>这里总结一下我们申请内存的流程:</p><p>用户<strong>使用 malloc 等函数来申请</strong>，然后系<strong>统分配一个虚拟地址</strong>(但是暂时不分配实际的物理内存)，然后操作系统如果是第一次访问这个新分配的虚拟地址的时候，发现<strong>如果还没有分配页表项</strong>，这个时候<strong>操作系统寻找一个新的物理页创建一个页表</strong>，然后<strong>建立当时分配的虚拟地址和页表(物理地址)之间的映射</strong></p><hr><h2 id="F-线程"><a href="#F-线程" class="headerlink" title="F 线程"></a>F 线程</h2><p>这里首先介绍线程，进程在后面介绍</p><hr><h3 id="F-1-什么是线程"><a href="#F-1-什么是线程" class="headerlink" title="F.1 什么是线程"></a>F.1 什么是线程</h3><p>还是引用书中的介绍：</p><p>在处理器数量不变的情况下，多任务操作系统采用了一种称为多道程序设计的方式，使处理器在所有任务之间来回切换，这样就给用户一种所有任务并行运行的错觉，通过<strong>任务调度器</strong>来实现</p><blockquote><p>任务调度器就是操作系统中用于把任务轮流调度上处理器运行的一个软件模块，它是操作系统的一部分。调度器在内核中维护一个任务表（也称进程表、线程表或调度表），然后按照一定的算法，从任务表中选择一个任务，然后把该任务放到处理器上运行，当任务运行的时间片到期后，再从任务表中找另外一个任务放到处理器上运行，周而复始，让任务表中的所有任务都有机会运行。正是因为有了调度器，多任务操作系统才能得以实现，它是多任务系统的核心，它的好坏直接影响了系统的效率</p></blockquote><p>优点是可以让每个任务都可以”同时”执行，缺点也有，那就是每次切换任务的时候也是需要耗费时间的，也就是会是整体的执行时间变长了一些，但这个代价可以使得一些重要紧急的任务及时完成</p><blockquote><p>处理器只知道加电后按照程序计数器中的地址不断地执行下去，在不断执行的过程中，我们把程序计数器中的下一条指令地址所组成的执行轨迹称为程序的控制执行流，让我们再深入描述一下。执行流就是一段逻辑上独立的指令区域，是人为给处理器安排的处理单元。指令是具备“能动性”的数据，因此只有指令才有“执行”的能力，它相当于是动作的发出者，由它指导处理器产生相应的行为。指令是由处理器来执行的，它引领处理器“前进”的方向，用“流”来表示处理器中程序计数器的航向，借此比喻处理器依次把此区域中的指令执行完后，所形成的像河流一样曲直不一的执行轨迹、执行路径（由顺序执行指令及跳转指令导致）<br><strong>执行流是独立的，它的独立性体现在每个执行流都有自己的栈、一套自己的寄存器映像和内存资源，这是 Intel 处理器在硬件上规定的，其实这正是执行流的上下文环境</strong></p></blockquote><hr><h3 id="F-2-进程的身份证–PCB"><a href="#F-2-进程的身份证–PCB" class="headerlink" title="F.2 进程的身份证–PCB"></a>F.2 进程的身份证–PCB</h3><p>虽然我们知道了大概得多任务操作系统，但是这里会有很多问题</p><blockquote><p>（1）要加载一个任务上处理器运行，任务由哪来？也就是说，调度器从哪里才能找到该任务？<br>（2）即使找到了任务，任务要在系统中运行，其所需要的资源从哪里获得？<br>（3）即使任务已经变成进程运行了，此进程应该运行多久呢？总不能让其独占处理器吧。<br>（4）即使知道何时将其换下处理器，那当前进程所使用的这一套资源（寄存器内容）应该存在哪里？<br>（5）进程被换下的原因是什么？下次调度器还能把它换上处理器运行吗？<br>（6）前面都说过了，进程独享地址空间，它的地址空间在哪里？<br>当然还有很多很多问题</p></blockquote><p>为了解决这些问题，操作系统为每个进程提供了一个PCB，即程序控制块(Process Control Block),用它来记录与这个进程相关的信息，比如优先级、PID、进程状态等等，同时PCB的具体格式取决于操作系统到功能复杂度</p><p>最后，操作系统单独维护一个进程表，将所有的PCB结构加载到这个表，然后由调度器来找对应的PCB，从而获取相关的信息，将寄存器映像(里面会保存进程的”现场”)加载到处理，然后新的进程就开始运行了，同时新进程的栈使用的也是PCB里面的栈</p><p>通过上面的描述，会发现PCB一般都很大，通常都是以页为单位，我们的这个简易操作系统比较小，就只占一页</p><hr><h3 id="F-3-实现线程的方式"><a href="#F-3-实现线程的方式" class="headerlink" title="F.3 实现线程的方式"></a>F.3 实现线程的方式</h3><p>这里有两种方式，一种是内核实现(0特权级)，一种是用户实现(3特权级)，但是两种都是为了用户进程服务的，所以线程必须可以运行用户的代码。下面主要介绍一下二者的优缺点:</p><ul><li><strong>用户程序实现</strong></li></ul><p><strong>优点</strong>:</p><ol><li>首先是容易移植，即便移动到一个不支持线程的操作系统上，同样也可以支持线程的用户程序</li><li>因为是用户程序自己实现，所以可以根据实际情况为某些线程加权调度</li><li>将线程的寄存器映像装载到 CPU 时，可以在用户空间完成，即不用陷入到内核态，这样就免去了进入内核时的入栈及出栈操作</li></ol><p><strong>缺点</strong>：</p><ol><li>进程中的某个线程出现了阻塞(通常由系统调用造成)，但是操作系统并不知道这个进程中有线程，于是将整个进程挂起，这样所有的线程都无法运行了</li><li>同时用户实现的时候，没有保险的机制使处理让出使用权给子线程，只能调用类似 pthread_yield 或 pthread_exit 之类的方法使线程发扬“高风亮节”让出处理器使用权，此类方法通过回调方式触发进程内的线程调度器，让调度器有机会选择进程内的其他线程上处理器运行</li><li>同时虽然减少了陷入内核态的步骤，相当于提速了，但是整个进程占用的处理器的时间片是有限的，再分给线程，那其实反而抵消了内部调度带来的提速</li></ol><hr><ul><li><strong>内核空间实现</strong></li></ul><p>注意，这里所说的“实现线程”是指由内核提供原生线程机制，用户进程中不再单独实现</p><p><strong>优点</strong>:</p><blockquote><p>相比在用户空间中实现线程，内核提供的线程相当于让进程多占了处理器资源，比如系统中运行有进程 A 和一传统型进程 B，此时进程 A 中显式创建了 3 个线程，这样一来，进程 A 加上主线程便有了 4 个线程，加上进程 B，内核调度器眼中便有了 5 个独立的执行流，尽管其中 4 个都属于进程 A，但对调度器来说这 4个线程和进程一样被调度，因此调度器调度完一圈后，进程 A 使用了 80%的处理器资源，这才是真正的提速</p></blockquote><ol><li>实现了较大幅度的提速，理由如上</li><li>当某一个线程阻塞的时候，因为是由内核空间实现的，所以只会阻塞这一个线程，其它线程并不受影响，也相当于提速</li></ol><p><strong>缺点</strong>：</p><ol><li>用户进程需要通过系统调用陷入内核，这多少增加了一些现场保护的栈操作，这还是会消耗<br>些处理器时间，但和上面的大幅度提速相比，这不算什么大事</li></ol><p><strong>以上就是两种实现方式，这里我们选择使用内核空间实现，还比较快</strong>~</p><h3 id="F-4-在内核空间实现线程"><a href="#F-4-在内核空间实现线程" class="headerlink" title="F.4 在内核空间实现线程"></a>F.4 在内核空间实现线程</h3><p>下面先来构造 PCB 相关的基础部分，这里添加一个文件夹<code>thread</code>和两个文件<code>thread.h</code> <code>thread.c</code>,下面还是直接看代码吧，我注释都写在代码里面了<br>同时Makefile也要更改，以后我会直接将修改的Makfile和添加的Makefile放在代码后面，大家可以自己展开参考代码:</p><h4 id="F4-1-PCB-线程的实现"><a href="#F4-1-PCB-线程的实现" class="headerlink" title="F4.1 PCB/线程的实现"></a>F4.1 PCB/线程的实现</h4><p>最后的效果就是可以一直打印创建的线程传入的参数，但现在还不能多线程，请大家慢慢往下看</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __KERNEL_THREAD_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __KERNEL_THREAD_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//自定义的通用函数类型，将在很多线程函数中作为形参</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span> <span class="title function_">thread_func</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//进程或线程的状态</span></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">task_status</span>{</span></span><br><span class="line">    TASK_RUNNING,</span><br><span class="line">    TASK_READY,</span><br><span class="line">    TASK_BLOCKED,</span><br><span class="line">    TASK_WAITNG,</span><br><span class="line">    TASK_HANGING,</span><br><span class="line">    TASK_DIED</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/*********** 中断栈 intr_stack ***************************** </span></span><br><span class="line"><span class="comment"> * 此结构用于中断发生时保护程序（线程或进程）的上下文环境: </span></span><br><span class="line"><span class="comment"> * 进程或线程被外部中断或软中断打断时，会按照此结构压入上下文</span></span><br><span class="line"><span class="comment"> * 寄存器，intr_exit 中的出栈操作是此结构的逆操作</span></span><br><span class="line"><span class="comment"> * 此栈在线程自己的内核栈中位置固定，所在页的最顶端</span></span><br><span class="line"><span class="comment"> ***********************************************************/</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">intr_stack</span>{</span> </span><br><span class="line">    <span class="type">uint32_t</span> vec_no;            <span class="comment">// kernel.S 宏 VECTOR 中 push %1 压入的中断号</span></span><br><span class="line">    <span class="type">uint32_t</span> edi; </span><br><span class="line">    <span class="type">uint32_t</span> esi; </span><br><span class="line">    <span class="type">uint32_t</span> ebp; </span><br><span class="line">    <span class="type">uint32_t</span> esp_dummy; </span><br><span class="line">    <span class="comment">// 虽然 pushad 把 esp 也压入，但 esp 是不断变化的，所以会被 popad 忽略</span></span><br><span class="line">    <span class="type">uint32_t</span> ebx; </span><br><span class="line">    <span class="type">uint32_t</span> edx; </span><br><span class="line">    <span class="type">uint32_t</span> ecx; </span><br><span class="line">    <span class="type">uint32_t</span> eax; </span><br><span class="line">    <span class="type">uint32_t</span> gs; </span><br><span class="line">    <span class="type">uint32_t</span> fs; </span><br><span class="line">    <span class="type">uint32_t</span> es; </span><br><span class="line">    <span class="type">uint32_t</span> ds; </span><br><span class="line">    <span class="comment">/* 以下由 cpu 从低特权级进入高特权级时压入 */</span> </span><br><span class="line">    <span class="type">uint32_t</span> err_code; <span class="comment">// err_code 会被压入在 eip 之后</span></span><br><span class="line">    <span class="type">void</span> (*eip) (<span class="type">void</span>); </span><br><span class="line">    <span class="type">uint32_t</span> cs; </span><br><span class="line">    <span class="type">uint32_t</span> eflags; </span><br><span class="line">    <span class="type">void</span>* esp; </span><br><span class="line">    <span class="type">uint32_t</span> ss; </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">thread_stack</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span> ebp; </span><br><span class="line">    <span class="type">uint32_t</span> ebx; </span><br><span class="line">    <span class="type">uint32_t</span> edi; </span><br><span class="line">    <span class="type">uint32_t</span> esi; </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 线程第一次执行时，eip 指向待调用的函数 kernel_thread其他时候，eip 是指向 switch_to 的返回地址*/</span> </span><br><span class="line">    <span class="comment">/* switch_to是将来用来实现任务切换的函数 */</span></span><br><span class="line">    <span class="type">void</span> (*eip) (thread_func* func, <span class="type">void</span>* func_arg); </span><br><span class="line"></span><br><span class="line">    <span class="comment">/************* 以下仅供第一次被调度上 cpu 时使用 ************/</span> </span><br><span class="line">    <span class="comment">/* 参数 unused_ret 只为占位置充数为返回地址 */</span> </span><br><span class="line">    <span class="type">void</span> (*unused_retaddr); </span><br><span class="line">    thread_func* function;  <span class="comment">// 由 kernel_thread 所调用的函数名</span></span><br><span class="line">    <span class="type">void</span>* func_arg;         <span class="comment">// 由 kernel_thread 所调用的函数所需的参数</span></span><br><span class="line">}; </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进程或线程的 pcb，程序控制块 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span>* self_kstack;      <span class="comment">// 各内核线程都用自己的内核栈</span></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">task_status</span> <span class="title">status</span>;</span> </span><br><span class="line">    <span class="type">uint8_t</span> priority;           <span class="comment">// 线程优先级</span></span><br><span class="line">    <span class="type">char</span> name[<span class="number">16</span>]; </span><br><span class="line">    <span class="type">uint32_t</span> stack_magic;       <span class="comment">//栈的边界标记，用于检测栈的溢出</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化线程栈---将待执行的函数和参数放到 thread_stack 中相应的位置</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_create</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,thread_func function,<span class="type">void</span> *func_arg)</span>;</span><br><span class="line"><span class="comment">//初始化线程的基本信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_thread</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,<span class="type">char</span>*name,<span class="type">int</span> prio)</span>;</span><br><span class="line"><span class="comment">//执行优先级为 prio 的线程，线程名为 name，线程执行的函数是 function(func_arg),返回线程的控制块</span></span><br><span class="line"><span class="keyword">struct</span> task_struct* <span class="title function_">thread_start</span><span class="params">(<span class="type">char</span>* name,</span></span><br><span class="line"><span class="params">                                 <span class="type">int</span> prio,</span></span><br><span class="line"><span class="params">                                 thread_func function,</span></span><br><span class="line"><span class="params">                                 <span class="type">void</span>* func_arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_SIZE 4096</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 由kernel_thread 去执行 function(func_arg)</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">kernel_thread</span><span class="params">(thread_func* function,<span class="type">void</span>* func_arg)</span></span><br><span class="line">{</span><br><span class="line">    function(func_arg);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化线程栈---将待执行的函数和参数放到 thread_stack 中相应的位置</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_create</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,thread_func function,<span class="type">void</span> *func_arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//先预留中断使用栈的空间，在thread.h中定义</span></span><br><span class="line">    pthread-&gt;self_kstack -= <span class="keyword">sizeof</span>(<span class="keyword">struct</span> intr_stack);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//再留出来线程的空间</span></span><br><span class="line">    pthread-&gt;self_kstack -= <span class="keyword">sizeof</span>(<span class="keyword">struct</span> thread_stack);</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">thread_stack</span>* <span class="title">kthread_stack</span> =</span> (<span class="keyword">struct</span> thread_stack*)pthread-&gt;self_kstack;    <span class="comment">//这个时候指向了栈的起始地址</span></span><br><span class="line">    kthread_stack-&gt;eip = kernel_thread;     <span class="comment">//线程首次调度从kernel_thread开始执行</span></span><br><span class="line">    kthread_stack-&gt;function = function;</span><br><span class="line">    kthread_stack-&gt;func_arg = func_arg;</span><br><span class="line">    kthread_stack-&gt;ebp = kthread_stack-&gt;ebx = kthread_stack-&gt;esi = kthread_stack-&gt;edi = <span class="number">0</span>;  <span class="comment">//设栈初始值为0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化线程的基本信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_thread</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,<span class="type">char</span>*name,<span class="type">int</span> prio)</span></span><br><span class="line">{</span><br><span class="line">    <span class="built_in">memset</span>(pthread,<span class="number">0</span>,<span class="keyword">sizeof</span>(*pthread)); <span class="comment">//先清零所有内容</span></span><br><span class="line">    <span class="built_in">strcpy</span>(pthread-&gt;name,name);         <span class="comment">//名称</span></span><br><span class="line">    pthread-&gt;status = TASK_RUNNING;     <span class="comment">//默认状态</span></span><br><span class="line">    pthread-&gt;priority = prio;           <span class="comment">//优先级</span></span><br><span class="line">    <span class="comment">//self_kstack是线程自己内核态下使用的栈顶地址</span></span><br><span class="line">    pthread-&gt;self_kstack = (<span class="type">uint32_t</span>*)((<span class="type">uint32_t</span>)pthread + PG_SIZE);</span><br><span class="line">    pthread-&gt;stack_magic = <span class="number">0x22332233</span>;  <span class="comment">//自定义的魔数</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//执行优先级为 prio 的线程，线程名为 name，线程执行的函数是 function(func_arg),返回线程的控制块</span></span><br><span class="line"><span class="keyword">struct</span> task_struct* <span class="title function_">thread_start</span><span class="params">(<span class="type">char</span>* name,</span></span><br><span class="line"><span class="params">                                 <span class="type">int</span> prio,</span></span><br><span class="line"><span class="params">                                 thread_func function,</span></span><br><span class="line"><span class="params">                                 <span class="type">void</span>* func_arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//pcb都位于内核空间，包括用户进程的pcb也在内核空间</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">thread</span> =</span> get_kernel_pages(<span class="number">1</span>);   <span class="comment">//获得1页空间</span></span><br><span class="line">    init_thread(thread,name,prio);                      <span class="comment">//初始化进程</span></span><br><span class="line">    thread_create(thread,function,func_arg);            <span class="comment">//指定函数，参数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//将栈指针（ESP）切换到新线程的栈顶</span></span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret"</span> : : <span class="string">"g"</span> (thread-&gt;self_kstack) : <span class="string">"memory"</span>)</span>;</span><br><span class="line">    <span class="keyword">return</span> thread;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有中断</span></span><br><span class="line">    <span class="comment">//创建一个线程，线程名，优先级，线程函数名，参数</span></span><br><span class="line">    thread_start(<span class="string">"k_thread_one"</span>,<span class="number">31</span>,k_thread_one,<span class="string">"argA"</span>);   </span><br><span class="line">    <span class="comment">// asm volatile("sti") ;        //暂时打开中断,这里先不打开</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        put_str(arg);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  子文件 /home/mouse/OS_mouse/tool/bochs/mouse/thread/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line">THREAD_SRCS := thread.c   <span class="comment"># 例如：thread.c, scheduler.c</span></span><br><span class="line">THREAD_ASMS :=            <span class="comment"># 例如：thread_switch.S</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果没有汇编文件，THREAD_ASMS 应为空</span></span><br></pre></td></tr></table></figure></div><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>主Makefile： 点击查看更多 </summary>              <div class="content">              <div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  内核构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/kernel/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># .PHONY 标明伪目标,它代表了一个需要被执行的动作或任务，而非一个需要被生成的文件</span></span><br><span class="line"><span class="comment"># 可以在本文件所在目录下 使用make all 、 make clean 等命令执行一系列任务</span></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: all kernel user clean img dirs</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 基础路径  =======================================</span></span><br><span class="line"><span class="comment"># 其中的 ":=" 表示立即展开使用，即右边的变量值会被立刻赋值，"$" 可以引用之前创建的变量</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line">MOUSE_DIR := ..</span><br><span class="line">BUILD_DIR := <span class="variable">$(MOUSE_DIR)</span>/build</span><br><span class="line">BUILD_KERNEL_DIR := <span class="variable">$(BUILD_DIR)</span>/kernel</span><br><span class="line">BUILD_USER_DIR := <span class="variable">$(BUILD_DIR)</span>/user</span><br><span class="line">IMG_PATH := /home/mouse/OS_mouse/tool/bochs/hd60M.img</span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 编译工具  =======================================</span></span><br><span class="line">CC := gcc</span><br><span class="line">ASM := nasm</span><br><span class="line">LD := ld</span><br><span class="line"></span><br><span class="line"><span class="comment"># C编译器标志，这里指出输出32位，编译器不识别/使用内建函数，禁用栈保护机制，指定当前为独立式环境(标准库不存在)，</span></span><br><span class="line"><span class="comment"># -I 分别包含需要的头文件路径(共用库，内核库，用户库，内核，设备)</span></span><br><span class="line">CFLAGS := -m32 -fno-builtin -fno-stack-protector -ffreestanding \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/lib -I<span class="variable">$(MOUSE_DIR)</span>/lib/kernel -I<span class="variable">$(MOUSE_DIR)</span>/lib/user \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/kernel -I<span class="variable">$(MOUSE_DIR)</span>/device -I<span class="variable">$(MOUSE_DIR)</span>/thread -c</span><br><span class="line">ASMFLAGS := -f elf</span><br><span class="line">LDFLAGS := -m elf_i386 -Ttext 0xc0001500 -e main</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===================== 包含子Makefile，导入子模块源文件  ===============================</span></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/device/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/thread/Makefile</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============================== 本目录源文件  ===========================================</span></span><br><span class="line">KERNEL_SRCS := main.c init.c interrupt.c debug.c memory.c</span><br><span class="line">KERNEL_ASMS := kernel.S</span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 为各模块添加前缀路径  =======================================</span></span><br><span class="line"><span class="comment"># $(addprefix &lt;prefix&gt;,&lt;names&gt;)是一个内置函数，能将路径prefix添加到names前</span></span><br><span class="line"><span class="comment"># ======================================================================================</span></span><br><span class="line">KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_SRCS)</span>)</span></span><br><span class="line">KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_SRCS)</span>)</span></span><br><span class="line">LIB_KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_USER_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_SRCS)</span>)</span></span><br><span class="line">LIB_USER_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">DEVICE_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_SRCS)</span>)</span></span><br><span class="line">DEVICE_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_SRCS)</span>)</span></span><br><span class="line">LIB_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">THREAD_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/thread/,<span class="variable">$(THREAD_SRCS)</span>)</span></span><br><span class="line">THREAD_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/thread/,<span class="variable">$(THREAD_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 汇总全部源文件  ================================================</span></span><br><span class="line">ALL_C_SRCS := <span class="variable">$(KERNEL_SRCS)</span> <span class="variable">$(LIB_KERNEL_SRCS)</span> <span class="variable">$(LIB_USER_SRCS)</span> <span class="variable">$(DEVICE_SRCS)</span> <span class="variable">$(LIB_SRCS)</span> <span class="variable">$(THREAD_SRCS)</span></span><br><span class="line">ALL_ASMS   := <span class="variable">$(KERNEL_ASMS)</span> <span class="variable">$(LIB_KERNEL_ASMS)</span> <span class="variable">$(LIB_USER_ASMS)</span> <span class="variable">$(DEVICE_ASMS)</span> <span class="variable">$(LIB_ASMS)</span> <span class="variable">$(THREAD_ASMS)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 生成对应的 .o 文件路径  ========================================</span></span><br><span class="line"><span class="comment"># $(filter &lt;pattern...&gt;,&lt;text&gt;)：这个函数用于从 &lt;text&gt;中筛选出符合模式 &lt;pattern&gt;的单词</span></span><br><span class="line"><span class="comment"># $(patsubst &lt;pattern&gt;,&lt;replacement&gt;,&lt;text&gt;)：将 &lt;text&gt;中所有匹配 &lt;pattern&gt;的单词替换为 </span></span><br><span class="line"><span class="comment"># &lt;replacement&gt;的形式，&lt;pattern&gt;中可以使用通配符 %</span></span><br><span class="line"><span class="comment"># 通过这个函数，将生成的.o文件分别生成到build路径下的不同路径(这里除了用户，都生成到/build/kernel)</span></span><br><span class="line"><span class="comment"># =========================================================================================</span></span><br><span class="line">OBJS_KERNEL := \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,<span class="variable">$(ALL_C_SRCS)</span>)</span>) \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,<span class="variable">$(ALL_ASMS)</span>)</span>)</span><br><span class="line"></span><br><span class="line">OBJS_USER := \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c,<span class="variable">$(BUILD_USER_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c,<span class="variable">$(ALL_C_SRCS)</span>)</span>) \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S,<span class="variable">$(BUILD_USER_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S,<span class="variable">$(ALL_ASMS)</span>)</span>)</span><br><span class="line"></span><br><span class="line">KERNEL_BIN := <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line"></span><br><span class="line"><span class="comment"># ==================================== 主要规则  ==========================================</span></span><br><span class="line"><span class="section">all: dirs kernel user</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 以@开头的指令，终端只会显示命令的输出，不显示命令本身--这里创建build文件夹</span></span><br><span class="line"><span class="section">dirs:</span></span><br><span class="line">@echo <span class="string">"Creating build directories..."</span></span><br><span class="line">@mkdir -p <span class="variable">$(BUILD_KERNEL_DIR)</span> <span class="variable">$(BUILD_USER_DIR)</span></span><br><span class="line"></span><br><span class="line"><span class="section">kernel: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line"><span class="section">user: <span class="variable">$(OBJS_USER)</span></span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(KERNEL_BIN)</span>: <span class="variable">$(OBJS_KERNEL)</span> <span class="variable">$(OBJS_USER)</span></span><br><span class="line">@echo <span class="string">"Linking kernel object files to generate kernel.bin..."</span></span><br><span class="line"><span class="variable">$(LD)</span> <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,<span class="variable">$^</span>)</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_USER_DIR)</span>/%.o,<span class="variable">$^</span>)</span></span><br><span class="line">@echo <span class="string">"Kernel linking completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================= 编译规则 ============================================</span></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling user C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling user S: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 镜像生成 ==========================================</span></span><br><span class="line"><span class="section">img: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line">@echo <span class="string">"Writing kernel image to disk..."</span></span><br><span class="line">dd if=<span class="variable">$(KERNEL_BIN)</span> of=<span class="variable">$(IMG_PATH)</span> bs=512 count=200 seek=9 conv=notrunc</span><br><span class="line">@echo <span class="string">"Kernel image writing completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 清理规则 ==========================================</span></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">@echo <span class="string">"Cleaning build files..."</span></span><br><span class="line">-rm -f <span class="variable">$(BUILD_KERNEL_DIR)</span>/*.o <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line">-rm -f <span class="variable">$(BUILD_USER_DIR)</span>/*.o</span><br><span class="line">@echo <span class="string">"Cleanup completed!"</span></span><br></pre></td></tr></table></figure></div>              </div>            </details><p>在学习多线程调度之前，我们还要学一个数据结构，就是大家数据结构学过的,双向链表，用来维护内核中的各种队列(比如进程的就绪队列，锁的等待队列等等……)</p><hr><h4 id="F4-2-双向链表"><a href="#F4-2-双向链表" class="headerlink" title="F4.2 双向链表"></a>F4.2 双向链表</h4><p>话不多说，直接开写，位置放在内核库下，也就是<code>/mouse/lib/kernel</code>,创建 <code>lsit.c</code> <code>list.h</code></p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/list.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_KERNEL_LIST_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_KERNEL_LIST_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> offset(struct_type,member) (int)(&amp;((struct_type*)0)-&gt;member)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> elem2entry(struct_type, struct_member_name, elem_ptr) \</span></span><br><span class="line"><span class="meta"> (struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name))</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定义链表节点成员结构 ---- 结点钟不需要数据成员，只要求前驱和后继节点指针</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>{</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">prev</span>;</span> <span class="comment">//前驱结点</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">next</span>;</span> <span class="comment">//后继结点</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 链表结构，用来实现队列</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">head</span>;</span>  <span class="comment">// head 是队首，固定不变，不是第一个元素，第一个元素是 head.next</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">tail</span>;</span>  <span class="comment">// tail 是队尾，同样不变</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//自定义函数类型function，用于list_traversal当做回调函数</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="title function_">bool</span> <span class="params">(function)</span><span class="params">(<span class="keyword">struct</span> list_elem*, <span class="type">int</span> arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//队列的具体实现</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">list_init</span> <span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">list_insert_before</span><span class="params">(<span class="keyword">struct</span> list_elem* before, <span class="keyword">struct</span> list_elem* elem)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">list_push</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, <span class="keyword">struct</span> list_elem* elem)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">list_iterate</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">list_append</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, <span class="keyword">struct</span> list_elem* elem)</span>;  </span><br><span class="line"><span class="type">void</span> <span class="title function_">list_remove</span><span class="params">(<span class="keyword">struct</span> list_elem* pelem)</span>;</span><br><span class="line"><span class="keyword">struct</span> list_elem* <span class="title function_">list_pop</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">list_empty</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist)</span>;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">list_len</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist)</span>;</span><br><span class="line"><span class="keyword">struct</span> list_elem* <span class="title function_">list_traversal</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, function func, <span class="type">int</span> arg)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">elem_find</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, <span class="keyword">struct</span> list_elem* obj_elem)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/list.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化双向链表</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">list_init</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* <span class="built_in">list</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="built_in">list</span>-&gt;head.prev = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="built_in">list</span>-&gt;head.next = &amp;<span class="built_in">list</span>-&gt;tail;</span><br><span class="line">    <span class="built_in">list</span>-&gt;tail.prev = &amp;<span class="built_in">list</span>-&gt;head;</span><br><span class="line">    <span class="built_in">list</span>-&gt;tail.next = <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把链表元素elem插入在元素before之前</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">list_insert_before</span><span class="params">(<span class="keyword">struct</span> list_elem* before,<span class="keyword">struct</span> list_elem* elem)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();   <span class="comment">//关闭中断</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//更新前驱元素的后继元素为elem，暂时让before脱离链表</span></span><br><span class="line">    before-&gt;prev-&gt;next = elem;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//更新elem自己的前驱元素为before的前驱</span></span><br><span class="line">    elem-&gt;prev = before-&gt;prev;</span><br><span class="line">    elem-&gt;next = before;        <span class="comment">//befroe回到链表</span></span><br><span class="line"></span><br><span class="line">    before-&gt;prev = elem;        <span class="comment">//更新before的前驱</span></span><br><span class="line"></span><br><span class="line">    intr_set_status(old_status);    <span class="comment">//打开中断</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加元素到列表队首,类似栈push操作</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">list_push</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, <span class="keyword">struct</span> list_elem* elem)</span> </span><br><span class="line">{</span><br><span class="line">   list_insert_before(plist-&gt;head.next, elem); <span class="comment">// 在队头插入elem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 追加元素到链表队尾,类似队列的先进先出操作</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">list_append</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, <span class="keyword">struct</span> list_elem* elem)</span> </span><br><span class="line">{</span><br><span class="line">   list_insert_before(&amp;plist-&gt;tail, elem);     <span class="comment">// 在队尾的前面插入</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 使元素pelem脱离链表 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">list_remove</span><span class="params">(<span class="keyword">struct</span> list_elem* pelem)</span> </span><br><span class="line">{</span><br><span class="line">   <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();</span><br><span class="line">   </span><br><span class="line">   pelem-&gt;prev-&gt;next = pelem-&gt;next;</span><br><span class="line">   pelem-&gt;next-&gt;prev = pelem-&gt;prev;</span><br><span class="line"></span><br><span class="line">   intr_set_status(old_status);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将链表第一个元素弹出并返回,类似栈的pop操作 */</span></span><br><span class="line"><span class="keyword">struct</span> list_elem* <span class="title function_">list_pop</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist)</span> </span><br><span class="line">{</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> =</span> plist-&gt;head.next;</span><br><span class="line">   list_remove(elem);</span><br><span class="line">   <span class="keyword">return</span> elem;</span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从链表中查找元素obj_elem,成功时返回true,失败时返回false */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">elem_find</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, <span class="keyword">struct</span> list_elem* obj_elem)</span> </span><br><span class="line">{</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> =</span> plist-&gt;head.next;</span><br><span class="line">   <span class="keyword">while</span> (elem != &amp;plist-&gt;tail) {</span><br><span class="line">      <span class="keyword">if</span> (elem == obj_elem) </span><br><span class="line">      {</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      }</span><br><span class="line">      elem = elem-&gt;next;</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 把列表plist中的每个元素elem和arg传给回调函数func,</span></span><br><span class="line"><span class="comment"> * arg给func用来判断elem是否符合条件.</span></span><br><span class="line"><span class="comment"> * 本函数的功能是遍历列表内所有元素,逐个判断是否有符合条件的元素。</span></span><br><span class="line"><span class="comment"> * 找到符合条件的元素返回元素指针,否则返回NULL. */</span></span><br><span class="line"><span class="keyword">struct</span> list_elem* <span class="title function_">list_traversal</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist, function func, <span class="type">int</span> arg)</span> </span><br><span class="line">{</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> =</span> plist-&gt;head.next;</span><br><span class="line">    <span class="comment">/* 如果队列为空,就必然没有符合条件的结点,故直接返回NULL */</span></span><br><span class="line">   <span class="keyword">if</span> (list_empty(plist)) </span><br><span class="line">   { </span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   }</span><br><span class="line"></span><br><span class="line">   <span class="keyword">while</span> (elem != &amp;plist-&gt;tail) </span><br><span class="line">   {</span><br><span class="line">      <span class="keyword">if</span> (func(elem, arg)) </span><br><span class="line">      {  <span class="comment">// func返回ture则认为该元素在回调函数中符合条件,命中,故停止继续遍历</span></span><br><span class="line">    <span class="keyword">return</span> elem;</span><br><span class="line">      }  <span class="comment">// 若回调函数func返回true,则继续遍历</span></span><br><span class="line">      elem = elem-&gt;next;       </span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回链表长度 */</span></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">list_len</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist)</span> </span><br><span class="line">{</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> =</span> plist-&gt;head.next;</span><br><span class="line">   <span class="type">uint32_t</span> length = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (elem != &amp;plist-&gt;tail) </span><br><span class="line">   {</span><br><span class="line">      length++; </span><br><span class="line">      elem = elem-&gt;next;</span><br><span class="line">   }</span><br><span class="line">   <span class="keyword">return</span> length;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断链表是否为空,空时返回true,否则返回false */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">list_empty</span><span class="params">(<span class="keyword">struct</span> <span class="built_in">list</span>* plist)</span> </span><br><span class="line">{<span class="comment">// 判断队列是否为空</span></span><br><span class="line">   <span class="keyword">return</span> (plist-&gt;head.next == &amp;plist-&gt;tail ? <span class="literal">true</span> : <span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>在实现双向链表之后就可以来进一步完善<code>thread.c</code>和<code>thread.h</code>了</p><h4 id="F4-3-简单优先级调度基础-任务调度器"><a href="#F4-3-简单优先级调度基础-任务调度器" class="headerlink" title="F4.3 简单优先级调度基础 & 任务调度器"></a>F4.3 简单优先级调度基础 &amp; 任务调度器</h4><p>这里添加了很多内容，我这里还是直接给出最终的文件，相比于之前的文件大家可以参考注释理解，这里只做简要说明:<br><strong>这个代码只要自己认真写/读一遍，基本都能理解原理</strong></p><p>完整的调度过程需要三部分的配合<br>（1）时钟中断处理函数<br>（2）调度器 schedule<br>（3）任务切换函数 switch_to</p><p>所以这里还是涉及到中断处理函数，包括注册等等，也同样写在下面并给出完整文件(主要修改<code>interrupt.c</code> <code>general_intr_handler()</code> <code>timer.c</code>,然后添加了时钟的中断处理函数等),同时优化了打印异常信息时候的打印方式(添加了指定位置的清屏)，也添加了设置光标的函数<code>print.S</code></p><p>第三步的任务切换函数还涉及到对上下文的保护，我们创建一个文件<code>switch.S</code>，放在thread文件夹下；</p><p>其中上下文保护分为两个部分，第一部分是保存任务进入中断前的全部寄存器，目的是能让任务恢复到中断前；第二部分是保存这 4 个寄存器：esi、edi、ebx 和 ebp，目的是让任务恢复执行在任务切换发生时剩下尚未执行的内核代码，保证顺利走到退出中断的出口，利用第一部分保护的寄存器环境彻底恢复任务，这里第一部分已经在kernel.S文件中由<code>intr%1entry</code>实现</p><p>最后在init中添加初始化，在主函数中添加两个进程编译运行后进行验证：</p><hr><h3 id="F-5-源码-修改部分"><a href="#F-5-源码-修改部分" class="headerlink" title="F.5 源码 & 修改部分"></a>F.5 源码 &amp; 修改部分</h3><p>话不多说，全是代码,在每个代码块上面简要说明修改添加了什么~</p><ul><li><strong>添加修改光标位置的函数 set_cursor( )</strong></li></ul><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/print.S</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------定义视频段的选择子---------------------------------</span></span><br><span class="line"><span class="comment">;一般要放在配置文件中，这里偷懒一下(因为只有三行)</span></span><br><span class="line">TI_GDT  <span class="built_in">equ</span>     <span class="number">0</span>   <span class="comment">;描述符表指示符​：0：使用全局描述符表；1：则表示使用局部描述符表​ </span></span><br><span class="line">RPL0    <span class="built_in">equ</span>     <span class="number">0</span>   <span class="comment">;请求特权级​：表示选择子的特权级。0是最高特权级（操作系统内核），还有1、2、3（用户通常为3）</span></span><br><span class="line">SELECTOR_VIDEO  <span class="built_in">equ</span> (<span class="number">0X0003</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">;​段选择子​：一个具体的值，用于在GDT中索引一个段描述符(0x0003表示全局描述符表的第四个描述符，也就是显存段的)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line">put_int_buffer <span class="built_in">dq</span> <span class="number">0</span> <span class="comment">; 定义 8 字节缓冲区用于数字到字符的转换</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------- put_int -----------------------</span></span><br><span class="line"><span class="comment">;功能: 将小端字节序的数字变成对应的 ASCII 后，倒置</span></span><br><span class="line"><span class="comment">;输入:栈中参数为待打印的数字</span></span><br><span class="line"><span class="comment">;输出:在屏幕上打印十六进制数字，并不会打印前缀 0x</span></span><br><span class="line"><span class="comment">;     如打印十进制 15 时，只会直接打印 f，不会是 0xf</span></span><br><span class="line"><span class="comment">;--------------------------------------------------------------</span></span><br><span class="line"><span class="meta">global</span> put_int      <span class="comment">;声明为外部可调用</span></span><br><span class="line"><span class="symbol">put_int:</span></span><br><span class="line">    <span class="keyword">pushad</span>          <span class="comment">; 保存所有通用寄存器状态</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebp</span>, <span class="built_in">esp</span>    <span class="comment">; 设置基址指针为当前栈顶</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, [<span class="built_in">ebp</span>+<span class="number">4</span> * <span class="number">9</span>]  <span class="comment">; 获取栈中参数（返回地址4字节 + pushad的8个4字节）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>, <span class="built_in">eax</span>    <span class="comment">; 复制参数到EDX用于处理</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edi</span>, <span class="number">7</span>      <span class="comment">; 设置缓冲区起始偏移（指向最高字节位置）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">8</span>      <span class="comment">; 循环计数器：32位数字对应8个十六进制位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, put_int_buffer <span class="comment">; EBX指向转换缓冲区基地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 将32位数字按十六进制从低位到高位处理</span></span><br><span class="line"><span class="symbol">.16based_4bits:</span>     <span class="comment">; 处理每个4位十六进制数字</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">edx</span>, <span class="number">0x0000000F</span>  <span class="comment">; 取当前最低4位</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edx</span>, <span class="number">9</span>      <span class="comment">; 判断是数字(0-9)还是字母(A-F)</span></span><br><span class="line">    <span class="keyword">jg</span> .is_A2F      <span class="comment">; 大于9则为字母</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>, <span class="string">'0'</span>    <span class="comment">; 转换为数字字符ASCII</span></span><br><span class="line">    <span class="keyword">jmp</span> .store</span><br><span class="line"><span class="symbol">.is_A2F:</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="built_in">edx</span>, <span class="number">10</span>     <span class="comment">; 计算字母偏移量（A-F对应10-15）</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>, <span class="string">'A'</span>    <span class="comment">; 转换为字母字符ASCII</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 将字符存储到缓冲区（高位字符在低地址）</span></span><br><span class="line"><span class="symbol">.store:</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">edi</span>], <span class="built_in">dl</span>   <span class="comment">; 存储转换后的字符</span></span><br><span class="line">    <span class="keyword">dec</span> <span class="built_in">edi</span>         <span class="comment">; 前移缓冲区位置（向低地址）</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>, <span class="number">4</span>      <span class="comment">; 右移4位处理下一个十六进制位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>, <span class="built_in">eax</span>    <span class="comment">; 更新EDX为剩余未处理部分</span></span><br><span class="line">    <span class="keyword">loop</span> .16based_4bits <span class="comment">; 循环处理所有8个十六进制位</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 准备打印：跳过高位连续的'0'</span></span><br><span class="line"><span class="symbol">.ready_to_print:</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">edi</span>         <span class="comment">; 调整EDI到第一个字符位置（之前为-1）</span></span><br><span class="line"><span class="symbol">.skip_prefix_0:</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edi</span>, <span class="number">8</span>      <span class="comment">; 检查是否已处理完所有字符</span></span><br><span class="line">    <span class="keyword">je</span> .full0       <span class="comment">; 如果全是0则跳转到全0处理</span></span><br><span class="line"><span class="symbol">.go_on_skip:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>, [put_int_buffer+<span class="built_in">edi</span>] <span class="comment">; 读取当前字符</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">edi</span>         <span class="comment">; 指向下一个字符</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>, <span class="string">'0'</span>     <span class="comment">; 检查是否为'0'</span></span><br><span class="line">    <span class="keyword">je</span> .skip_prefix_0 <span class="comment">; 如果是'0'则继续跳过</span></span><br><span class="line">    <span class="keyword">dec</span> <span class="built_in">edi</span>         <span class="comment">; 回退到第一个非0字符位置</span></span><br><span class="line">    <span class="keyword">jmp</span> .put_each_num <span class="comment">; 开始打印非0部分</span></span><br><span class="line"><span class="symbol">.full0:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>, <span class="string">'0'</span>     <span class="comment">; 全0情况只打印单个'0'</span></span><br><span class="line"><span class="symbol">.put_each_num:</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>        <span class="comment">; 压栈字符参数供put_char使用</span></span><br><span class="line">    <span class="keyword">call</span> put_char   <span class="comment">; 调用字符打印函数</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>, <span class="number">4</span>      <span class="comment">; 清理栈空间</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">edi</span>         <span class="comment">; 指向缓冲区下一个字符</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>, [put_int_buffer+<span class="built_in">edi</span>] <span class="comment">; 读取下一个字符</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edi</span>, <span class="number">8</span>      <span class="comment">; 检查是否已打印所有字符</span></span><br><span class="line">    <span class="keyword">jl</span> .put_each_num <span class="comment">; 继续打印直到结束</span></span><br><span class="line">    <span class="keyword">popad</span>           <span class="comment">; 恢复所有通用寄存器</span></span><br><span class="line">    <span class="keyword">ret</span>             <span class="comment">; 函数返回</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------- put_str -----------------------</span></span><br><span class="line"><span class="comment">;功能: 通过put_char打印以0字符结尾的字符串</span></span><br><span class="line"><span class="comment">;输入:栈中参数为打印的字符串</span></span><br><span class="line"><span class="comment">;输出:无</span></span><br><span class="line"><span class="comment">;--------------------------------------------------------------</span></span><br><span class="line"><span class="meta">global</span> put_str      <span class="comment">;声明为外部可调用</span></span><br><span class="line"><span class="symbol">put_str:</span></span><br><span class="line">    <span class="comment">;由于本函数中只用到了 ebx 和 ecx，只备份这两个寄存器</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ebx</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ecx</span>,<span class="built_in">ecx</span>         <span class="comment">;清零</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,[<span class="built_in">esp</span>+<span class="number">12</span>]    <span class="comment">;备份的2个寄存器+返回地址(8+2)</span></span><br><span class="line"><span class="symbol">.goon:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>,[<span class="built_in">ebx</span>]</span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>,<span class="number">0</span>    <span class="comment">;如果处理到了字符串末尾('0')，则跳转到结束处返回 </span></span><br><span class="line">    <span class="keyword">jz</span> .str_over</span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>    <span class="comment">;为put_char函数传递参数</span></span><br><span class="line">    <span class="keyword">call</span> put_char</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">4</span>   <span class="comment">;回收参数所占用的空间</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">ebx</span>     <span class="comment">;让ebx指向下一个字符</span></span><br><span class="line">    <span class="keyword">jmp</span> .goon   <span class="comment">;循环判断字符串直到末尾</span></span><br><span class="line"><span class="symbol">.str_over:</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ebx</span>     <span class="comment">;还原寄存器</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ecx</span>     </span><br><span class="line">    <span class="keyword">ret</span>         <span class="comment">;返回</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------- put_char -----------------------</span></span><br><span class="line"><span class="comment">;功能: 将栈中的一个字符写入光标所在处</span></span><br><span class="line"><span class="comment">;--------------------------------------------------------------</span></span><br><span class="line"><span class="meta">global</span> put_char     <span class="comment">;声明为外部可调用</span></span><br><span class="line"><span class="symbol">put_char:</span></span><br><span class="line">    <span class="keyword">pushad</span>      <span class="comment">;备份32位寄存器环境 push all double,指压入所有双字节长的寄存器(32个字节)</span></span><br><span class="line">                <span class="comment">;需保证gs为正确的视频段选择子，为保险，每次打印都为其赋值</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,SELECTOR_VIDEO       <span class="comment">;不能直接将立即数送入段寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">gs</span>,<span class="built_in">ax</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;------------------------------ 获取当前光标位置 ------------------------------</span></span><br><span class="line">    <span class="comment">;先或获得高八位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x03d4</span>   <span class="comment">;索引寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>,<span class="number">0x0e</span>     <span class="comment">;用于提供光标位置的高八位</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span>       <span class="comment">;将索引 0xe 写入索引寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x03d5</span>   <span class="comment">;通过读写0x3d5来获得或设置光标位置</span></span><br><span class="line">    <span class="keyword">in</span>  <span class="built_in">al</span>,<span class="built_in">dx</span>       <span class="comment">;得到了光标位置的高八位,读入al</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="number">ah</span>,<span class="built_in">al</span>       <span class="comment">;在写入ah寄存器(in指令要求源操作数和目标数必须一样都是8位/16位，所以这里多移动一次)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;再获取低 8 位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0f</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span> </span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">al</span>, <span class="built_in">dx</span> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>,<span class="built_in">ax</span>       <span class="comment">;将光标存入 bx(常用于bx位基址寻址)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;--------------------- 获取待打印字符并判断类型-&gt;跳转相关函数  --------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span> ,[<span class="built_in">esp</span>+<span class="number">36</span>]   <span class="comment">;返回地址占 4 字节,pushad占 32 字节 所以共36字节，然后就可以获得待打印的字符</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>,<span class="number">0xd</span>      <span class="comment">;CR(回车)是0x0d</span></span><br><span class="line">    <span class="keyword">jz</span> .is_carriage_return</span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>, <span class="number">0xa</span>     <span class="comment">;LF(换行)是0x0a，这里二者都处理成回车换行(CRLF)</span></span><br><span class="line">    <span class="keyword">jz</span> .is_line_feed</span><br><span class="line"></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>,<span class="number">0x8</span>      <span class="comment">;BS(backspace 退格)</span></span><br><span class="line">    <span class="keyword">jz</span> .is_backspace</span><br><span class="line">    <span class="keyword">jmp</span> .put_other  <span class="comment">;其余字符</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;--------------------------------- 相关函数的实现  -------------------------------</span></span><br><span class="line"><span class="comment">;----- 退格 -----</span></span><br><span class="line"><span class="symbol">.is_backspace:</span></span><br><span class="line">    <span class="comment">;这里退格之后，还添加了空格或者空字符0，这样就会覆盖后面的字符</span></span><br><span class="line">    <span class="keyword">dec</span> <span class="built_in">bx</span>      <span class="comment">;bx--(光标--)</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">bx</span>,<span class="number">1</span>    <span class="comment">;bx&lt;&lt;1(相当于乘2,即转换为显存中的字节偏移量)</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>],<span class="number">0x20</span>   <span class="comment">;填充字节为0或者空格(0x20是空格)</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">bx</span>                  <span class="comment">;bx++ 写入属性</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>], <span class="number">0x07</span>  <span class="comment">;黑底白字</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">bx</span>,<span class="number">1</span>                <span class="comment">;bx&gt;&gt;1,恢复光标值</span></span><br><span class="line">    <span class="keyword">jmp</span> .set_cursor         <span class="comment">;更新光标位置</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 其它字符 -----</span></span><br><span class="line"><span class="symbol">.put_other:</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">bx</span>,<span class="number">1</span>    <span class="comment">;bx&lt;&lt;1(相当于乘2,即转换为显存中的字节偏移量)</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>],<span class="built_in">cl</span>     <span class="comment">;填充字节为0或者空格(0x20是空格)</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">bx</span>                  <span class="comment">;bx++ 写入属性</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>], <span class="number">0x07</span>  <span class="comment">;黑底白字</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">bx</span>,<span class="number">1</span>                <span class="comment">;bx&gt;&gt;1,恢复光标值</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">bx</span>                  <span class="comment">;bx++,下一个光标值</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">bx</span>, <span class="number">2000</span>            <span class="comment">;如果小于2000，表示未写到显存的末尾，则去设置光标值</span></span><br><span class="line">                            <span class="comment">;如果超过2000，表示需要换行</span></span><br><span class="line">    <span class="keyword">jl</span> .set_cursor         <span class="comment">;更新光标位置（如果小于2000）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 回车换行 -----</span></span><br><span class="line"><span class="symbol">.is_line_feed:</span>              <span class="comment">;换行(\n)</span></span><br><span class="line"><span class="symbol">.is_carriage_return:</span>        <span class="comment">;回车(\r) 光标移动到行首，这里统一都写成回车换行</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">dx</span>,<span class="built_in">dx</span>               <span class="comment">;dx是被除数的高16位，清零</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">bx</span>               <span class="comment">;ax是被除数的低16位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">si</span>,<span class="number">80</span>               <span class="comment">;每行是80字符</span></span><br><span class="line">    <span class="keyword">div</span> <span class="built_in">si</span>                  <span class="comment">;计算行位置</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="built_in">bx</span>,<span class="built_in">dx</span>               <span class="comment">;光标值减去除 80 的余数便是取整</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.is_carriage_return_end:</span>    <span class="comment">;回车符处理结束</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">bx</span>,<span class="number">80</span>               <span class="comment">;光标移动到下一行</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">bx</span>,<span class="number">2000</span>             <span class="comment">;检查是否超出屏幕范围(80x25=2000)</span></span><br><span class="line"><span class="symbol">.is_line_feed_end:</span></span><br><span class="line">    <span class="keyword">jl</span> .set_cursor          <span class="comment">;如果bx&lt;2000则跳转到设置光标位置</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 滚屏 -----  </span></span><br><span class="line"><span class="comment">;这里有两种方案，第一种是是要设置起始地址寄存器，优点是可以缓存16KB个字符，屏幕外的文本也可以很快的找回</span></span><br><span class="line"><span class="comment">;               第二种是固定屏幕，,直接搬运，优点是不用设置起始地址寄存器，缺点是只能缓存2000个字符</span></span><br><span class="line"><span class="comment">;这里书中介绍的是第二种，因为简单方便，易于理解</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;第二种滚屏方法:</span></span><br><span class="line"><span class="comment">;1.将第 1～24 行的内容整块搬到第 0～23 行，也就是把第 0 行的数据覆盖。</span></span><br><span class="line"><span class="comment">;2.再将第 24 行，也就是最后一行的字符用空格覆盖，这样它看上去是一个新的空行。</span></span><br><span class="line"><span class="comment">;3.把光标移到第 24 行也就是最后一行行首</span></span><br><span class="line"><span class="comment">;搬运1~24行</span></span><br><span class="line"><span class="symbol">.roll_screen:</span>       <span class="comment">;若超出屏幕大小，开始滚屏</span></span><br><span class="line">    <span class="keyword">cld</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">960</span>     <span class="comment">;要搬运2000-80个字符，1920*2=3840字节</span></span><br><span class="line">                    <span class="comment">;一次搬运4字节，也就是960次</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="number">0xc00b80a0</span>  <span class="comment">;第1行行首---复制起始地址</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edi</span>,<span class="number">0xc00b8000</span>  <span class="comment">;第0行行首---复制目标地址</span></span><br><span class="line">    <span class="keyword">rep</span> <span class="keyword">movsd</span>           <span class="comment">;逐4字节复制(32)</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, <span class="number">3840</span>       <span class="comment">;最后一行首字符的第一个字节的偏移1920*2</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">80</span>         <span class="comment">;一行80个字符，一次一个字符(2字节)，要移动80次a</span></span><br><span class="line"><span class="comment">;清理最后一行</span></span><br><span class="line"><span class="symbol">.cls:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">word</span> [<span class="built_in">gs</span>:<span class="built_in">ebx</span>],<span class="number">0x0720</span>    <span class="comment">;0x0720是黑底白字的空格键</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,<span class="number">2</span>           <span class="comment">;一次一个字符(字+属性)</span></span><br><span class="line">    <span class="keyword">loop</span> .cls           <span class="comment">;循环清理80次</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>,<span class="number">1920</span>         <span class="comment">;设置光标为1920,也就是最后一行的首字符   </span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 设置光标 ----- </span></span><br><span class="line"><span class="symbol">.set_cursor:</span></span><br><span class="line"><span class="comment">;将光标设置为bx值</span></span><br><span class="line">    <span class="comment">;---- 设置高8位 ----</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span>  <span class="comment">;索引寄存器    </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0e</span>    <span class="comment">;用于提供光标位置的高 8 位</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span>  <span class="comment">;通过读写数据端口 0x3d5 来获得或设置光标位置</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">bh</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line">    <span class="comment">;---- 设置低8位 ----</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0f</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="built_in">bl</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line"><span class="comment">;函数结尾</span></span><br><span class="line"><span class="symbol">.put_char_done:</span> </span><br><span class="line">    <span class="keyword">popad</span>   <span class="comment">;恢复寄存器环境</span></span><br><span class="line">    <span class="keyword">ret</span>     <span class="comment">;返回</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">global</span> set_cursor   <span class="comment">;声明外部可以调用，用来设置光标的位置 使用方法: set_cursor(number)</span></span><br><span class="line"><span class="symbol">set_cursor:</span></span><br><span class="line">    <span class="keyword">pushad</span>          <span class="comment">;保存寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>,[<span class="built_in">esp</span>+<span class="number">36</span>] <span class="comment">;获取修改的位置</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;先设置高8位</span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span>  <span class="comment">;索引寄存器</span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0e</span>  <span class="comment">;用于提供光标位置的高8位</span></span><br><span class="line">   <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span>  <span class="comment">;通过读写数据端口0x3d5来获得或设置光标位置 </span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">bh</span></span><br><span class="line">   <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;;;;;;; 2 再设置低8位 ;;;;;;;;;</span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0f</span></span><br><span class="line">   <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span> </span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="built_in">bl</span></span><br><span class="line">   <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line">   <span class="keyword">popad</span></span><br><span class="line">   <span class="keyword">ret</span></span><br></pre></td></tr></table></figure></div><p>注意在print.h文件中声明一下刚刚添加的汇编函数</p><ul><li><p><strong>添加中断错误的显示逻辑(在通用中断处理函数中)，使用刚刚的设置光标功能</strong></p></li><li><p><strong>添加中断的注册函数<code>register_handler( )</code></strong></p></li></ul><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span>              <span class="comment">//put_str</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span>                 <span class="comment">//io操作,联合汇编</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_CNT 0x33       <span class="comment">// 目前总共支持的中断数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_CTRL 0x20         <span class="comment">// 主片的控制端口是 0x20 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_DATA 0x21         <span class="comment">// 主片的数据端口是 0x21 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_CTRL 0xa0         <span class="comment">// 从片的控制端口是 0xa0 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_DATA 0xa1         <span class="comment">// 从片的数据端口是 0xa1</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_IF 0X00000200    <span class="comment">//eflags 寄存器的 if 位为0</span></span></span><br><span class="line"><span class="comment">//获取 eflags 寄存器的值,EFLAG_VAR 是返回值</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GET_EFLAGS(EFLAG_VAR)   asm volatile(<span class="string">"pushfl; popl %0"</span> : <span class="string">"=g"</span> (EFLAG_VAR))</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 中断门描述符结构体</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span>  <span class="title">gate_desc</span>{</span></span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_low_word;</span><br><span class="line">    <span class="type">uint16_t</span>    selector;</span><br><span class="line">    <span class="type">uint8_t</span>     dcount;     <span class="comment">//此项为双字计数字段,是门描述符中的第4字节。为固定值,不用考虑</span></span><br><span class="line">    <span class="type">uint8_t</span>     attribute;</span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_high_word;     </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态函数声明,非必须</span></span><br><span class="line"><span class="comment">// intr_handler 是自己定义的(interrupt.h) typedef void* intr_handler；</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span>;</span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">gate_desc</span> <span class="title">idt</span>[<span class="title">IDT_DESC_CNT</span>];</span>              <span class="comment">// idt是中断描述符,本质上一个中断门描述符数组</span></span><br><span class="line"><span class="keyword">extern</span> intr_handler intr_entry_table[IDT_DESC_CNT];     <span class="comment">// 声明引用定义kernel.S中的中断处理函数入口函数 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">//添加部分:</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exception_init</span><span class="params">(<span class="type">void</span>)</span>;                   <span class="comment">//完成一般中断处理函数注册及异常名称注册</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">general_intr_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span>;   <span class="comment">//通用的中断处理函数,一般在异常出现时处理</span></span><br><span class="line"><span class="type">const</span> <span class="type">char</span>* intr_name[IDT_DESC_CNT];                <span class="comment">//用于保留异常的名字</span></span><br><span class="line">intr_handler idt_table[IDT_DESC_CNT];               <span class="comment">//中断处理函数程序数组,在kernel.S 中定义的 intrXXentry是中断处理函数的入口,最终调用 idt_table 里面的函数</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化可编程中断控制器 8259A */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">pic_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/*初始化主片 */</span> </span><br><span class="line">    outb (PIC_M_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x20</span>); <span class="comment">// ICW2: 起始中断向量号为 0x20 </span></span><br><span class="line">    <span class="comment">//也就是 IR[0-7] 为 0x20 ～ 0x27 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x04</span>); <span class="comment">// ICW3: IR2 接从片</span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line">    <span class="comment">/*初始化从片 */</span> </span><br><span class="line">    outb (PIC_S_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x28</span>); <span class="comment">// ICW2: 起始中断向量号为 0x28 </span></span><br><span class="line">    <span class="comment">// 也就是 IR[8-15]为 0x28 ～ 0x2F </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x02</span>); <span class="comment">// ICW3: 设置从片连接到主片的 IR2 引脚</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/*打开主片上 IR0,也就是目前只接受时钟产生的中断 */</span> </span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0xfe</span>); </span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0xff</span>); </span><br><span class="line">    put_str(<span class="string">" pic_init done\n"</span>); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*创建中断门描述符*/</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 参数: 中断门描述符的指针,中断描述符内的属性,中断描述符内对应的中断处理函数</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span></span><br><span class="line">{</span><br><span class="line">    p_gdesc-&gt;func_offset_low_word = (<span class="type">uint32_t</span>)function &amp; <span class="number">0x0000FFFF</span>;</span><br><span class="line">    p_gdesc-&gt;selector = SELECTOR_K_CODE;        <span class="comment">//定义在global.h : 指向内核数据段的选择子</span></span><br><span class="line">    p_gdesc-&gt;dcount = <span class="number">0</span>;</span><br><span class="line">    p_gdesc-&gt;attribute = attr;</span><br><span class="line">    p_gdesc-&gt;func_offset_high_word = ((<span class="type">uint32_t</span>)function &amp; <span class="number">0Xffff0000</span>) &gt;&gt;<span class="number">16</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*初始化中断描述符*/</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">idt_desc_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        make_idt_desc(&amp;idt[i],IDT_DESC_ATTR_DPL0,intr_entry_table[i]);</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"idt_desc_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//添加部分</span></span><br><span class="line"><span class="comment">//通用的中断处理函数,一般在异常出现时处理</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">general_intr_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(vec_nr == <span class="number">0x27</span> || vec_nr == <span class="number">0x2f</span>)    <span class="comment">//IRQ7s和IRQ15会产生伪中断,无需处理,0x2f是从片8259A上的最后一个IRQ引脚,保留项</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将光标置为0，从屏幕清理出一片打印异常信息的区域，方便阅读</span></span><br><span class="line">    set_cursor(<span class="number">0</span>);</span><br><span class="line">    <span class="type">int</span> cursor_pos = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(cursor_pos &lt;  <span class="number">320</span> )</span><br><span class="line">    {</span><br><span class="line">        put_char(<span class="string">' '</span>);  <span class="comment">//空格</span></span><br><span class="line">        cursor_pos++;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    set_cursor(<span class="number">0</span>);      <span class="comment">//重新设置为左上角</span></span><br><span class="line">    put_str(<span class="string">"!!!!!!! excetion message begin !!!!!!!!\n"</span>); </span><br><span class="line"></span><br><span class="line">    set_cursor(<span class="number">88</span>); <span class="comment">// 从第 2 行第 8 个字符开始打印</span></span><br><span class="line"></span><br><span class="line">    put_str((<span class="type">char</span>*)intr_name[vec_nr]);     <span class="comment">//打印名字哦</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (vec_nr == <span class="number">14</span>)   <span class="comment">// 若为 Pagefault，将缺失的地址打印出来并悬停，缺页异常</span></span><br><span class="line">    { </span><br><span class="line">        <span class="type">int</span> page_fault_vaddr = <span class="number">0</span>; </span><br><span class="line">        <span class="keyword">asm</span> (<span class="string">"movl %%cr2, %0"</span> : <span class="string">"=r"</span> (page_fault_vaddr));     <span class="comment">// cr2 是存放造成 page_fault 的地址</span></span><br><span class="line">        put_str(<span class="string">"\npage fault addr is "</span>);put_int(page_fault_vaddr); </span><br><span class="line">    } </span><br><span class="line">    put_str(<span class="string">"\n!!!!!!! excetion message end !!!!!!!!\n"</span>); </span><br><span class="line">    <span class="comment">// 能进入中断处理程序就表示已经处在关中断情况下</span></span><br><span class="line">    <span class="comment">// 不会出现调度进程的情况。故下面的死循环不会再被中断</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//页中断,我当时调试使用的,大家可以不用管,现在移植到上面的通用中断中判断</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">page_fault_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(vec_nr == <span class="number">0x27</span> || vec_nr == <span class="number">0x2f</span>)    <span class="comment">//IRQ7s和IRQ15会产生伪中断,无需处理,0x2f是从片8259A上的最后一个IRQ引脚,保留项</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"int vector : 0x"</span>);</span><br><span class="line">    put_int(vec_nr);</span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> faulting_address;</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"mov %%cr2, %0"</span> : <span class="string">"=r"</span> (faulting_address))</span>; <span class="comment">// 读取CR2</span></span><br><span class="line">    put_str(<span class="string">"Page fault at address: 0x"</span>);</span><br><span class="line">    put_int(faulting_address);  <span class="comment">// 十六进制打印</span></span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//完成一般中断处理函数注册及异常名称注册</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exception_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">/* idt_table 数组中的函数是在进入中断后根据中断向量号调用的 */</span></span><br><span class="line">        <span class="comment">/* 见 kernel/kernel.S 的 call [idt_table + %1*4] */</span> </span><br><span class="line">        idt_table[i] = general_intr_handler;    <span class="comment">// 默认为 general_intr_handler ,以后会由 register_handler 来注册具体处理函数</span></span><br><span class="line">        intr_name[i] = <span class="string">"unknown"</span>;               <span class="comment">// 先统一赋值为 unknow</span></span><br><span class="line">    }</span><br><span class="line">    <span class="comment">// idt_table[14] = page_fault_handler;         //页错误处理函数</span></span><br><span class="line">    intr_name[<span class="number">0</span>] = <span class="string">"#DE Divide Error"</span>; </span><br><span class="line">    intr_name[<span class="number">1</span>] = <span class="string">"#DB Debug Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">2</span>] = <span class="string">"NMI Interrupt"</span>; </span><br><span class="line">    intr_name[<span class="number">3</span>] = <span class="string">"#BP Breakpoint Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">4</span>] = <span class="string">"#OF Overflow Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">5</span>] = <span class="string">"#BR BOUND Range Exceeded Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">6</span>] = <span class="string">"#UD Invalid Opcode Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">7</span>] = <span class="string">"#NM Device Not Available Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">8</span>] = <span class="string">"#DF Double Fault Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">9</span>] = <span class="string">"Coprocessor Segment Overrun"</span>; </span><br><span class="line">    intr_name[<span class="number">10</span>] = <span class="string">"#TS Invalid TSS Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">11</span>] = <span class="string">"#NP Segment Not Present"</span>;</span><br><span class="line">    intr_name[<span class="number">12</span>] = <span class="string">"#SS Stack Fault Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">13</span>] = <span class="string">"#GP General Protection Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">14</span>] = <span class="string">"#PF Page-Fault Exception"</span>; </span><br><span class="line">    <span class="comment">// intr_name[15] 第 15 项是 intel 保留项,未使用</span></span><br><span class="line">    intr_name[<span class="number">16</span>] = <span class="string">"#MF x87 FPU Floating-Point Error"</span>; </span><br><span class="line">    intr_name[<span class="number">17</span>] = <span class="string">"#AC Alignment Check Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">18</span>] = <span class="string">"#MC Machine-Check Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">19</span>] = <span class="string">"#XF SIMD Floating-Point Exception"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*开中断并且返回开中断前的状态 intr_statusl类型定义再interrupt.h中 */</span></span><br><span class="line"><span class="comment">/*这里在每个if分支都添加return语句，可以让程序更加快，但更大，空间换时间*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_enable</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span>;</span>    </span><br><span class="line">    <span class="keyword">if</span>(INTR_ON == intr_get_status())</span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_ON;</span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_OFF;</span><br><span class="line">        <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"sti"</span>)</span>;    <span class="comment">//开中断,sti指令将IF置1</span></span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*关中断并且返回关中断前的状态 */</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_disable</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span>;</span></span><br><span class="line">    <span class="keyword">if</span>(INTR_ON == intr_get_status())</span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_ON;</span><br><span class="line">        <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"cli"</span> : : : <span class="string">"memory"</span>)</span>; <span class="comment">//关中断,cli指令将IF位置置0</span></span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        old_status = INTR_OFF;</span><br><span class="line">        <span class="keyword">return</span> old_status;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*将中断状态设置成 status*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_set_status</span><span class="params">(<span class="keyword">enum</span> intr_status status)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> (status &amp; INTR_ON) ?intr_enable() : intr_disable();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*获取当前中断状态*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_get_status</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span> eflags = <span class="number">0</span>;</span><br><span class="line">    GET_EFLAGS(eflags);     <span class="comment">//宏实现</span></span><br><span class="line">    <span class="keyword">return</span> (EFLAGS_IF &amp; eflags) ? INTR_ON : INTR_OFF;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//注册安装中断处理函数，vector_no表示安装在中断处理程序数组的第几个元素中，function是中断处理函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">register_handler</span><span class="params">(<span class="type">uint8_t</span> vector_no, intr_handler function)</span></span><br><span class="line">{</span><br><span class="line">    idt_table[vector_no] = function;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*完成有关中断的所有初始化工作*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"idt_init start\n"</span>);</span><br><span class="line">    idt_desc_init();                <span class="comment">//初始化中断描述符表</span></span><br><span class="line">    exception_init();               <span class="comment">//初始化异常名并注册通常的中断处理函数</span></span><br><span class="line">    pic_init();                     <span class="comment">//初始化8259A</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载idt</span></span><br><span class="line">    <span class="type">uint64_t</span> idt_operand = ((<span class="keyword">sizeof</span>(idt) - <span class="number">1</span>) | ((<span class="type">uint64_t</span>)((<span class="type">uint32_t</span>)idt &lt;&lt; <span class="number">16</span>)));</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"lidt %0"</span> ::<span class="string">""</span>(idt_operand))</span>;</span><br><span class="line">    put_str(<span class="string">"idt_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><strong>头文件添加声明</strong></li></ul><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __INTERRUPUT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __INTERRUPUT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span>* intr_handler;</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span>;</span><br><span class="line"><span class="comment">//注册安装中断处理函数，vector_no表示安装在中断处理程序数组的第几个元素中，function是中断处理函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">register_handler</span><span class="params">(<span class="type">uint8_t</span> vector_no, intr_handler function)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*定义中断的两种状态</span></span><br><span class="line"><span class="comment"> *  INTR_OFF 值为0，表示关中断</span></span><br><span class="line"><span class="comment"> *  INTR_ON  值为1，表示开中断</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span>{</span>   <span class="comment">//中断状态</span></span><br><span class="line">    INTR_OFF,</span><br><span class="line">    INTR_ON</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_enable</span><span class="params">()</span>;              <span class="comment">/*开中断并且返回开中断前的状态*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_disable</span><span class="params">()</span>;             <span class="comment">/*关中断并且返回关中断前的状态 */</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_get_status</span><span class="params">()</span>;          <span class="comment">/*获取当前中断状态*/</span></span><br><span class="line"><span class="keyword">enum</span> intr_status <span class="title function_">intr_set_status</span><span class="params">(<span class="keyword">enum</span> intr_status status)</span>;       <span class="comment">/*将中断状态设置成 status*/</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><ul><li><p><strong>添加全局的时间片变量ticks</strong></p></li><li><p><strong>添加时钟中断处理函数，并且在初始化中进行注册</strong></p></li><li><p><strong>中断处理函数负责对时间片–，并根据情况触发任务调度器</strong></p></li></ul><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/timer.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"timer.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IRQ0_FREQUENCY 100          <span class="comment">//100HZ</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> INPUT_FREQUENCY 1193180     <span class="comment">//计数器0的工作脉冲信号频率</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY     <span class="comment">//通过宏来计算初值</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONTRER0_PORT 0x40      <span class="comment">//端口2</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER0_NO 0           <span class="comment">//计数器0</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER_MODE 2          <span class="comment">//工作模式 方式2</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> READ_WRITE_LATCH 3      <span class="comment">//读写方式，3表示先读写低8位，再读写高8位</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIT_CONTROL_PORT 0x43   <span class="comment">//控制字寄存器的端口</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> ticks;                 <span class="comment">//ticks是内核自中断开启以来的总共的滴答数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 把操作的计数器 counter_no､ 读写锁属性 rwl､ 计数器模式 counter_mode 写入模式控制寄存器并赋予初始值 counter_value </span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">frequency_set</span><span class="params">(<span class="type">uint8_t</span> counter_port,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> counter_no,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> rwl,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> counter_mode,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint16_t</span> counter_value)</span> </span><br><span class="line">{ </span><br><span class="line"><span class="comment">//往控制字寄存器端口 0x43 中写入控制字</span></span><br><span class="line">    outb(PIT_CONTROL_PORT, (<span class="type">uint8_t</span>)(counter_no &lt;&lt; <span class="number">6</span> | rwl &lt;&lt; <span class="number">4</span> | counter_mode &lt;&lt; <span class="number">1</span>)); </span><br><span class="line"><span class="comment">//先写入 counter_value 的低 8 位</span></span><br><span class="line">    outb(counter_port, (<span class="type">uint8_t</span>)counter_value); </span><br><span class="line"><span class="comment">//再写入 counter_value 的高 8 位 </span></span><br><span class="line">    outb(counter_port, (<span class="type">uint8_t</span>)counter_value &gt;&gt; <span class="number">8</span>); </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">//时钟的中断函数</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">intr_timer_handler</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur_thread</span> =</span> running_thread(); <span class="comment">//获取当前正在运行的线程，将其复制给PCB指针cur_thread</span></span><br><span class="line"></span><br><span class="line">    ASSERT(cur_thread-&gt;stack_magic == <span class="number">0x22332233</span>);      <span class="comment">//检查堆栈是否溢出</span></span><br><span class="line"></span><br><span class="line">    cur_thread-&gt;elapsed_ticks++;                        <span class="comment">//记录此线程占用cpu的时间</span></span><br><span class="line">    ticks++;                                            <span class="comment">//内核从第一次进入中断至今的时间</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(cur_thread-&gt;ticks == <span class="number">0</span>)                          <span class="comment">//进程的时间片用完，则开始调度新的进程上cpu</span></span><br><span class="line">    {</span><br><span class="line">        schedule();     <span class="comment">//调度器</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        cur_thread-&gt;ticks--;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化 PIT8253</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">timer_init</span><span class="params">()</span> </span><br><span class="line">{ </span><br><span class="line">    put_str(<span class="string">"timer_init start\n"</span>); </span><br><span class="line">    <span class="comment">//设置 8253 的定时周期，也就是发中断的周期</span></span><br><span class="line">    frequency_set(CONTRER0_PORT, \</span><br><span class="line">                    COUNTER0_NO, \</span><br><span class="line">                    READ_WRITE_LATCH,\</span><br><span class="line">                    COUNTER_MODE, \</span><br><span class="line">                    COUNTER0_VALUE); </span><br><span class="line"></span><br><span class="line">    register_handler(<span class="number">0x20</span>,intr_timer_handler);     <span class="comment">//注册时钟的中断处理函数</span></span><br><span class="line">    put_str(<span class="string">"timer_init done\n"</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><p><strong>添加任务调度器函数，并且添加 完善main为主线程 的函数</strong></p></li><li><p><strong>修改初始化函数</strong></p></li><li><p><strong>修改任务开始的逻辑，引入主函数线程判断等等</strong></p></li></ul><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PG_SIZE 4096</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">main_thread</span>;</span>    <span class="comment">//主进程PCB</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">thread_ready_list</span>;</span>      <span class="comment">//就绪队列</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">thread_all_list</span>;</span>        <span class="comment">//所有任务队列</span></span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">thread_tag</span>;</span>        <span class="comment">//用于保存队列中的线程结点</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="type">void</span> <span class="title function_">switch_to</span><span class="params">(<span class="keyword">struct</span> task_struct* cur, <span class="keyword">struct</span> task_struct* next)</span>;   <span class="comment">//实现任务切换</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//获取当前任务的PCB指针</span></span><br><span class="line"><span class="keyword">struct</span> task_struct* <span class="title function_">running_thread</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">uint32_t</span> esp;</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"mov %%esp,%0"</span>:<span class="string">"=g"</span> (esp))</span>;   </span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">struct</span> task_struct*)(esp &amp; <span class="number">0xfffff000</span>);  <span class="comment">//取esp整数部分，高20位</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 由kernel_thread 去执行 function(func_arg)</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">kernel_thread</span><span class="params">(thread_func* function,<span class="type">void</span>* func_arg)</span></span><br><span class="line">{</span><br><span class="line">    intr_enable();      <span class="comment">//执行前打开中断，防止时钟中断被屏蔽，从而无法调度其它线程</span></span><br><span class="line">    function(func_arg);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化线程栈---将待执行的函数和参数放到 thread_stack 中相应的位置</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_create</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,thread_func function,<span class="type">void</span> *func_arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//先预留中断使用栈的空间，在thread.h中定义</span></span><br><span class="line">    pthread-&gt;self_kstack -= <span class="keyword">sizeof</span>(<span class="keyword">struct</span> intr_stack);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//再留出来线程的空间</span></span><br><span class="line">    pthread-&gt;self_kstack -= <span class="keyword">sizeof</span>(<span class="keyword">struct</span> thread_stack);</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">thread_stack</span>* <span class="title">kthread_stack</span> =</span> (<span class="keyword">struct</span> thread_stack*)pthread-&gt;self_kstack;    <span class="comment">//这个时候指向了栈的起始地址</span></span><br><span class="line">    kthread_stack-&gt;eip = kernel_thread;     <span class="comment">//线程首次调度从kernel_thread开始执行</span></span><br><span class="line">    kthread_stack-&gt;function = function;</span><br><span class="line">    kthread_stack-&gt;func_arg = func_arg;</span><br><span class="line">    kthread_stack-&gt;ebp = kthread_stack-&gt;ebx = kthread_stack-&gt;esi = kthread_stack-&gt;edi = <span class="number">0</span>;  <span class="comment">//设栈初始值为0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化线程的基本信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_thread</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,<span class="type">char</span>*name,<span class="type">int</span> prio)</span></span><br><span class="line">{</span><br><span class="line">    <span class="built_in">memset</span>(pthread,<span class="number">0</span>,<span class="keyword">sizeof</span>(*pthread)); <span class="comment">//先清零所有内容</span></span><br><span class="line">    <span class="built_in">strcpy</span>(pthread-&gt;name,name);         <span class="comment">//名称</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//由于把main函数也封装成一个线程，并且也是一直运行的，所以直接设置成TASK_RUNNING,其余设置成就绪态</span></span><br><span class="line">    <span class="keyword">if</span>(pthread == main_thread)</span><br><span class="line">    {</span><br><span class="line">        pthread-&gt;status = TASK_RUNNING;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        pthread-&gt;status = TASK_READY;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">//self_kstack是线程自己内核态下使用的栈顶地址</span></span><br><span class="line">    pthread-&gt;self_kstack = (<span class="type">uint32_t</span>*)((<span class="type">uint32_t</span>)pthread + PG_SIZE);</span><br><span class="line">    pthread-&gt;priority = prio;           <span class="comment">//优先级</span></span><br><span class="line">    pthread-&gt;ticks = prio;              <span class="comment">//时间片</span></span><br><span class="line">    pthread-&gt;elapsed_ticks = <span class="number">0</span>;         <span class="comment">//0表示还没有开始运行</span></span><br><span class="line">    pthread-&gt;pgdir = <span class="literal">NULL</span>;</span><br><span class="line">    pthread-&gt;stack_magic = <span class="number">0x22332233</span>;  <span class="comment">//自定义的魔数，同时它也是栈的边界</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//执行优先级为 prio 的线程，线程名为 name，线程执行的函数是 function(func_arg),返回线程的控制块</span></span><br><span class="line"><span class="keyword">struct</span> task_struct* <span class="title function_">thread_start</span><span class="params">(<span class="type">char</span>* name,</span></span><br><span class="line"><span class="params">                                 <span class="type">int</span> prio,</span></span><br><span class="line"><span class="params">                                 thread_func function,</span></span><br><span class="line"><span class="params">                                 <span class="type">void</span>* func_arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//pcb都位于内核空间，包括用户进程的pcb也在内核空间</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">thread</span> =</span> get_kernel_pages(<span class="number">1</span>);   <span class="comment">//获得1页空间</span></span><br><span class="line">    init_thread(thread,name,prio);                      <span class="comment">//初始化进程</span></span><br><span class="line">    thread_create(thread,function,func_arg);            <span class="comment">//指定函数，参数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//确保之前不在就绪队列中</span></span><br><span class="line">    ASSERT(!elem_find(&amp;thread_ready_list,&amp;thread-&gt;general_tag));</span><br><span class="line">    list_append(&amp;thread_ready_list,&amp;thread-&gt;general_tag);   <span class="comment">//加入就绪线程队列</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//确保之前不在全部线程队列中</span></span><br><span class="line">    ASSERT(!elem_find(&amp;thread_all_list,&amp;thread-&gt;all_list_tag));</span><br><span class="line">    list_append(&amp;thread_all_list,&amp;thread-&gt;all_list_tag);   <span class="comment">//加入全部线程队列</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//将栈指针（ESP）切换到新线程的栈顶</span></span><br><span class="line">    <span class="comment">//asm volatile ("movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret" : : "g" (thread-&gt;self_kstack) : "memory");</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> thread;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 下面来将kernel中的main函数完善为主线程</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_main_thread</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//main线程很早就已经运行了，所以loader.S 中进入内核时的 mov esp,0xc009f00</span></span><br><span class="line">    <span class="comment">//就是为其预留的PCB,因此pcb的地址就是 0xc009e000</span></span><br><span class="line">    <span class="comment">//所以不需用get_kernel_pages获得1页空间</span></span><br><span class="line">    main_thread = running_thread(); <span class="comment">//获取当前任务指针</span></span><br><span class="line">    init_thread(main_thread,<span class="string">"main"</span>,<span class="number">31</span>); </span><br><span class="line"></span><br><span class="line">    <span class="comment">//main函数是当前线程,当前线程不在thread_ready_list中</span></span><br><span class="line">    <span class="comment">//所以只用将它添加在thread_all_list中</span></span><br><span class="line">    ASSERT(!elem_find(&amp;thread_all_list,&amp;main_thread-&gt;all_list_tag));    <span class="comment">//确保之前不在全部线程队列中   </span></span><br><span class="line">    list_append(&amp;thread_all_list,&amp;main_thread-&gt;all_list_tag);           <span class="comment">//加入全部线程队列</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//任务调度器</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">schedule</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line">    </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> =</span> running_thread(); <span class="comment">//获取当前进程PCB</span></span><br><span class="line">    <span class="keyword">if</span>(cur-&gt;status == TASK_RUNNING)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">//若这个线程知识cpu时间片到了，就加入到就绪队列尾</span></span><br><span class="line">        ASSERT(!elem_find(&amp;thread_ready_list,&amp;cur-&gt;general_tag));</span><br><span class="line">        list_append(&amp;thread_ready_list,&amp;cur-&gt;general_tag);</span><br><span class="line">        cur-&gt;ticks = cur-&gt;priority; <span class="comment">//重新设置ticks为priority</span></span><br><span class="line">        cur-&gt;status = TASK_READY;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        <span class="comment">//什么都不用干，因为已经不在运行了，并且当前线程也不在就绪队列中</span></span><br><span class="line">        <span class="comment">//只用取出就绪列表的下一个任务即可</span></span><br><span class="line">    }</span><br><span class="line">    ASSERT(!list_empty(&amp;thread_ready_list));    <span class="comment">//就绪列表不为空</span></span><br><span class="line">    thread_tag = <span class="literal">NULL</span>;                          <span class="comment">//thread_tag清空</span></span><br><span class="line">    <span class="comment">//准备将就绪队列中的第一个就绪线程弹出，并放入CPU</span></span><br><span class="line">    thread_tag = list_pop(&amp;thread_ready_list);</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">next</span> =</span> elem2entry(<span class="keyword">struct</span> task_struct,general_tag,thread_tag);   <span class="comment">//寻找弹出的就绪队列的PCB结构体头</span></span><br><span class="line">    next-&gt;status = TASK_RUNNING;</span><br><span class="line">    switch_to(cur,next);    <span class="comment">//切换进程</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化线程</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"thread_init start\n"</span>);</span><br><span class="line"></span><br><span class="line">    list_init(&amp;thread_ready_list);</span><br><span class="line">    list_init(&amp;thread_all_list);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将当前的main函数创建为线程</span></span><br><span class="line">    make_main_thread();</span><br><span class="line">    put_str(<span class="string">"thread_init done"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><strong>添加时间调度器的声明，以及完善thread.h中的部分结构体</strong></li></ul><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/thread.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __KERNEL_THREAD_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __KERNEL_THREAD_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//自定义的通用函数类型，将在很多线程函数中作为形参</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span> <span class="title function_">thread_func</span><span class="params">(<span class="type">void</span>*)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//进程或线程的状态</span></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">task_status</span>{</span></span><br><span class="line">    TASK_RUNNING,</span><br><span class="line">    TASK_READY,</span><br><span class="line">    TASK_BLOCKED,</span><br><span class="line">    TASK_WAITNG,</span><br><span class="line">    TASK_HANGING,</span><br><span class="line">    TASK_DIED</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/*********** 中断栈 intr_stack ***************************** </span></span><br><span class="line"><span class="comment"> * 此结构用于中断发生时保护程序（线程或进程）的上下文环境: </span></span><br><span class="line"><span class="comment"> * 进程或线程被外部中断或软中断打断时，会按照此结构压入上下文</span></span><br><span class="line"><span class="comment"> * 寄存器，intr_exit 中的出栈操作是此结构的逆操作</span></span><br><span class="line"><span class="comment"> * 此栈在线程自己的内核栈中位置固定，所在页的最顶端</span></span><br><span class="line"><span class="comment"> ***********************************************************/</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">intr_stack</span>{</span> </span><br><span class="line">    <span class="type">uint32_t</span> vec_no;            <span class="comment">// kernel.S 宏 VECTOR 中 push %1 压入的中断号</span></span><br><span class="line">    <span class="type">uint32_t</span> edi; </span><br><span class="line">    <span class="type">uint32_t</span> esi; </span><br><span class="line">    <span class="type">uint32_t</span> ebp; </span><br><span class="line">    <span class="type">uint32_t</span> esp_dummy; </span><br><span class="line">    <span class="comment">// 虽然 pushad 把 esp 也压入，但 esp 是不断变化的，所以会被 popad 忽略</span></span><br><span class="line">    <span class="type">uint32_t</span> ebx; </span><br><span class="line">    <span class="type">uint32_t</span> edx; </span><br><span class="line">    <span class="type">uint32_t</span> ecx; </span><br><span class="line">    <span class="type">uint32_t</span> eax; </span><br><span class="line">    <span class="type">uint32_t</span> gs; </span><br><span class="line">    <span class="type">uint32_t</span> fs; </span><br><span class="line">    <span class="type">uint32_t</span> es; </span><br><span class="line">    <span class="type">uint32_t</span> ds; </span><br><span class="line">    <span class="comment">/* 以下由 cpu 从低特权级进入高特权级时压入 */</span> </span><br><span class="line">    <span class="type">uint32_t</span> err_code; <span class="comment">// err_code 会被压入在 eip 之后</span></span><br><span class="line">    <span class="type">void</span> (*eip) (<span class="type">void</span>); </span><br><span class="line">    <span class="type">uint32_t</span> cs; </span><br><span class="line">    <span class="type">uint32_t</span> eflags; </span><br><span class="line">    <span class="type">void</span>* esp; </span><br><span class="line">    <span class="type">uint32_t</span> ss; </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">thread_stack</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span> ebp; </span><br><span class="line">    <span class="type">uint32_t</span> ebx; </span><br><span class="line">    <span class="type">uint32_t</span> edi; </span><br><span class="line">    <span class="type">uint32_t</span> esi; </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 线程第一次执行时，eip 指向待调用的函数 kernel_thread其他时候，eip 是指向 switch_to 的返回地址*/</span> </span><br><span class="line">    <span class="comment">/* switch_to是将来用来实现任务切换的函数 */</span></span><br><span class="line">    <span class="type">void</span> (*eip) (thread_func* func, <span class="type">void</span>* func_arg); </span><br><span class="line"></span><br><span class="line">    <span class="comment">/************* 以下仅供第一次被调度上 cpu 时使用 ************/</span> </span><br><span class="line">    <span class="comment">/* 参数 unused_ret 只为占位置充数为返回地址 */</span> </span><br><span class="line">    <span class="type">void</span> (*unused_retaddr); </span><br><span class="line">    thread_func* function;  <span class="comment">// 由 kernel_thread 所调用的函数名</span></span><br><span class="line">    <span class="type">void</span>* func_arg;         <span class="comment">// 由 kernel_thread 所调用的函数所需的参数</span></span><br><span class="line">}; </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进程或线程的 pcb，程序控制块 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> {</span> </span><br><span class="line">    <span class="type">uint32_t</span>* self_kstack;      <span class="comment">// 各内核线程都用自己的内核栈,注意为首位</span></span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">task_status</span> <span class="title">status</span>;</span>    <span class="comment">// 任务状态</span></span><br><span class="line">    <span class="type">uint8_t</span> priority;           <span class="comment">// 线程优先级</span></span><br><span class="line">    <span class="type">char</span> name[<span class="number">16</span>];              <span class="comment">// 名称</span></span><br><span class="line">    <span class="type">uint8_t</span> ticks;              <span class="comment">// 每次在处理器上执行的时间滴答数,每次时钟中断都会对其减1，到0则换下处理器</span></span><br><span class="line">    <span class="type">uint32_t</span> elapsed_ticks;     <span class="comment">// 从cpu运行至今占用了多少cpu滴答数</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">general_tag</span>;</span>       <span class="comment">//用于线程在一般队列的结点  </span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">all_list_tag</span>;</span>      <span class="comment">//用于线程队列thread_all_list的结点</span></span><br><span class="line">    <span class="type">uint32_t</span>* pgdir;                    <span class="comment">//进程自己的虚拟地址     </span></span><br><span class="line">    <span class="type">uint32_t</span> stack_magic;               <span class="comment">// 栈的边界标记，用于检测栈的溢出，注意为尾部</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化线程栈---将待执行的函数和参数放到 thread_stack 中相应的位置</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_create</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,thread_func function,<span class="type">void</span> *func_arg)</span>;</span><br><span class="line"><span class="comment">//初始化线程的基本信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_thread</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread,<span class="type">char</span>*name,<span class="type">int</span> prio)</span>;</span><br><span class="line"><span class="comment">//执行优先级为 prio 的线程，线程名为 name，线程执行的函数是 function(func_arg),返回线程的控制块</span></span><br><span class="line"><span class="keyword">struct</span> task_struct* <span class="title function_">thread_start</span><span class="params">(<span class="type">char</span>* name,</span></span><br><span class="line"><span class="params">                                 <span class="type">int</span> prio,</span></span><br><span class="line"><span class="params">                                 thread_func function,</span></span><br><span class="line"><span class="params">                                 <span class="type">void</span>* func_arg)</span>;</span><br><span class="line"><span class="keyword">struct</span> task_struct* <span class="title function_">running_thread</span><span class="params">()</span>;<span class="comment">//获取当前任务的PCB指针</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">schedule</span><span class="params">()</span>;    <span class="comment">//任务调度器</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_init</span><span class="params">(<span class="type">void</span>)</span>; <span class="comment">//初始化线程</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><ul><li><strong>添加切换任务的函数–同时添加保护上下文的汇编代码</strong></li></ul><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/thread/switch.S</span></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line"><span class="meta">global</span> switch_to</span><br><span class="line"><span class="symbol">switch_to:</span></span><br><span class="line">   <span class="comment">;栈中此处是返回地址       </span></span><br><span class="line">   <span class="keyword">push</span> <span class="built_in">esi</span></span><br><span class="line">   <span class="keyword">push</span> <span class="built_in">edi</span></span><br><span class="line">   <span class="keyword">push</span> <span class="built_in">ebx</span></span><br><span class="line">   <span class="keyword">push</span> <span class="built_in">ebp</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">eax</span>, [<span class="built_in">esp</span> + <span class="number">20</span>]        <span class="comment">; 得到栈中的参数cur, cur = [esp+20]</span></span><br><span class="line">   <span class="keyword">mov</span> [<span class="built_in">eax</span>], <span class="built_in">esp</span>                   <span class="comment">; 保存栈顶指针esp. task_struct的self_kstack字段,</span></span><br><span class="line">                    <span class="comment">; self_kstack在task_struct中的偏移为0,</span></span><br><span class="line">                    <span class="comment">; 所以直接往thread开头处存4字节便可。</span></span><br><span class="line">                                    </span><br><span class="line"><span class="comment">;------------------  以上是备份当前线程的环境，下面是恢复下一个线程的环境  ----------------</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">eax</span>, [<span class="built_in">esp</span> + <span class="number">24</span>]    <span class="comment">; 得到栈中的参数next, next = [esp+24]</span></span><br><span class="line">   <span class="keyword">mov</span> <span class="built_in">esp</span>, [<span class="built_in">eax</span>]        <span class="comment">; pcb的第一个成员是self_kstack成员,用来记录0级栈顶指针,</span></span><br><span class="line">                <span class="comment">; 用来上cpu时恢复0级栈,0级栈中保存了进程或线程所有信息,包括3级栈指针</span></span><br><span class="line">   <span class="keyword">pop</span> <span class="built_in">ebp</span></span><br><span class="line">   <span class="keyword">pop</span> <span class="built_in">ebx</span></span><br><span class="line">   <span class="keyword">pop</span> <span class="built_in">edi</span></span><br><span class="line">   <span class="keyword">pop</span> <span class="built_in">esi</span></span><br><span class="line">   <span class="keyword">ret</span>    <span class="comment">; 返回到上面switch_to下面的那句注释的返回地址,</span></span><br><span class="line">        <span class="comment">; 未由中断进入,第一次执行时会返回到kernel_thread</span></span><br></pre></td></tr></table></figure></div><ul><li><strong>添加主初始化中线程初始化函数</strong></li></ul><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/init.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"timer.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化所有模块</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_all</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"init_all\n"</span>);</span><br><span class="line">    idt_init();             <span class="comment">//中断</span></span><br><span class="line">    timer_init();           <span class="comment">//定时器设备 PIT</span></span><br><span class="line">    mem_init();             <span class="comment">//内存管理初始化</span></span><br><span class="line">    thread_init();          <span class="comment">//线程初始化</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><strong>在主函数中调用创建线程，最后观察结果</strong></li></ul><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有</span></span><br><span class="line">    <span class="comment">//创建一个线程，线程名，优先级，线程函数名，参数</span></span><br><span class="line">    thread_start(<span class="string">"k_thread_one"</span>,<span class="number">31</span>,k_thread_one,<span class="string">"argA"</span>);   </span><br><span class="line">    thread_start(<span class="string">"k_thread_two"</span>,<span class="number">31</span>,k_thread_two,<span class="string">"argB"</span>);</span><br><span class="line"></span><br><span class="line">    intr_enable();      <span class="comment">//打开中断，使时钟中断起作用</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        put_str(<span class="string">"Main"</span>);</span><br><span class="line">    }   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        put_str(arg);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        put_str(arg);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>注意，在编译的时候如果有报错，可以检查是不是缺少头文件包含，或者是声明等等</p><p>然后直接运行，就可以发现三个任务可以交替运行（交替打印出不同的字符串），但是会发现两个问题:</p><ul><li><strong>有时候三个打印的字符串可能会错开、有空格、间断、等等</strong></li><li><strong>会发生报错，#GP General Protection Exception，一般保护性异常</strong></li></ul><p>这两个都会在下一章的<strong>同步机制</strong>中讲解，所以下一章见~</p><hr><h2 id="G-同步"><a href="#G-同步" class="headerlink" title="G. 同步"></a>G. 同步</h2><p>这里从上一节出现的报错，以及错误混乱现象来入手，其实主要是因为在打印的逻辑中:</p><ol><li>获取光标</li><li>光标转化为字节地址，在该地址写入字符</li><li>更新光标</li></ol><p>这三个任务其实必须要一起执行，否则如果中途被切换了就会出现打印错误的打印情况，这其实就是原子(不可再分)的体现，所以这里我们可以通过开关中断的情况来保证原子性</p><p>为了方便，这里只用在put_str的前后添加开关中断,这里修改主函数:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有</span></span><br><span class="line">    <span class="comment">//创建一个线程，线程名，优先级，线程函数名，参数</span></span><br><span class="line">    thread_start(<span class="string">"k_thread_one"</span>,<span class="number">31</span>,k_thread_one,<span class="string">"AAAA "</span>);   </span><br><span class="line">    thread_start(<span class="string">"k_thread_two"</span>,<span class="number">8</span>,k_thread_two,<span class="string">"BBBB "</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// intr_disable();         //关中断</span></span><br><span class="line">    <span class="comment">// intr_enable();      //打开中断，使时钟中断起作用</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        intr_disable();         <span class="comment">//关中断</span></span><br><span class="line">        put_str(<span class="string">"MMMM "</span>);</span><br><span class="line">        intr_enable();          <span class="comment">//开中断</span></span><br><span class="line">    }   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        intr_disable();         <span class="comment">//关中断</span></span><br><span class="line">        put_str(arg);</span><br><span class="line">        intr_enable();          <span class="comment">//开中断</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        intr_disable();         <span class="comment">//关中断</span></span><br><span class="line">        put_str(arg);</span><br><span class="line">        intr_enable();          <span class="comment">//开中断</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>其实这里还有一个问题，是我遇到的，就是<strong>如果输出的字符串是5位，比如”11111”,三个任务输出长度都需要一样，那么可以正常执行<br>但是如果修改长度，则会报错，不限于错误操作码等</strong></p><p>初步原因是因为恰好5位的时候避开了同时写入么？这里还没有找到原因，打算通过完成锁机制后再查看<br>（好像找到了，vaddr_get，这个函数之前写的有问题，现在已经修改了）</p><blockquote><p><strong>当正确添加锁机制(使用互斥锁实现控制台输出)之后便没有了问题了</strong></p></blockquote><p>同时检查代码的时候发现之前<code>idt_init</code>中有个有个位置写错了，在下面指出:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*完成有关中断的所有初始化工作*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"idt_init start\n"</span>);</span><br><span class="line">    idt_desc_init();                <span class="comment">//初始化中断描述符表</span></span><br><span class="line">    exception_init();               <span class="comment">//初始化异常名并注册通常的中断处理函数</span></span><br><span class="line">    pic_init();                     <span class="comment">//初始化8259A</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载idt</span></span><br><span class="line">    <span class="comment">// uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)((uint32_t)idt &lt;&lt; 16)));</span></span><br><span class="line">    <span class="comment">// asm volatile("lidt %0" ::""(idt_operand));</span></span><br><span class="line">    <span class="comment">// put_str("idt_init done\n");</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//修改成下面的样子</span></span><br><span class="line"></span><br><span class="line">   <span class="type">uint64_t</span> idt_operand = ((<span class="keyword">sizeof</span>(idt) - <span class="number">1</span>) | ((<span class="type">uint64_t</span>)(<span class="type">uint32_t</span>)idt &lt;&lt; <span class="number">16</span>));</span><br><span class="line">   <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"lidt %0"</span> : : <span class="string">"m"</span> (idt_operand))</span>;</span><br><span class="line">   put_str(<span class="string">"idt_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="G-1-锁-信号量"><a href="#G-1-锁-信号量" class="headerlink" title="G.1 锁/信号量"></a>G.1 锁/信号量</h3><p>这个概念相信很多人都了解过，这里还是简要介绍一下，然后具体看代码:</p><h4 id="G1-1-概念"><a href="#G1-1-概念" class="headerlink" title="G1.1 概念"></a>G1.1 概念</h4><blockquote><p>信号量就是个计数器，它的计数值是自然数，用来记录所积累信号的数量。这里的信号是个泛指，取决于信号量的实际应用环境</p></blockquote><blockquote><p>因此对信号量的加法操作是用 up 表示，减法操作是用 down 表示。下面介绍下这两种操作。增加操作 up 包括两个微操作。<br>（1）将信号量的值加 1。<br>（2）唤醒在此信号量上等待的线程。<br>减少操作 down 包括三个子操作。<br>（1）判断信号量是否大于 0。<br>（2）若信号量大于 0，则将信号量减 1。<br>（3）若信号量等于 0，当前线程将自己阻塞，以在此信号量上等待。<br>信号量是个全局共享变量，up 和 down 又都是读写这个全局变量的操作，而且它们都包含一系列的子操作，因此它们必须都是原子操作</p></blockquote><h4 id="G1-2-实现线程的阻塞与唤醒"><a href="#G1-2-实现线程的阻塞与唤醒" class="headerlink" title="G1.2 实现线程的阻塞与唤醒"></a>G1.2 实现线程的阻塞与唤醒</h4><p>还是在<code>thread.c</code>中实现，主要是添加了两个函数，下面给出函数，别忘了在头文件声明</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//当前线程阻塞，并且标志其状态为stat</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_block</span><span class="params">(<span class="keyword">enum</span> task_status stat)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//stat 取值为 TASK_BLOCKED、TASK_WAITING、TASK_HANGING，</span></span><br><span class="line">    ASSERT( (stat == TASK_BLOCKED) || (stat == TASK_WAITNG) ||(stat == TASK_HANGING) );</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();   <span class="comment">//关中断</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur_thread</span> =</span> running_thread();  <span class="comment">//获取当前线程pcb</span></span><br><span class="line">    cur_thread-&gt;status = stat;</span><br><span class="line">    schedule();                 <span class="comment">//调度器</span></span><br><span class="line">    <span class="comment">//等待当前线程解除阻塞后继续运行下面的intr_set_status</span></span><br><span class="line">    intr_set_status(old_status);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//解除pthread线程的阻塞状态</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_unblock</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable(); <span class="comment">//关中断</span></span><br><span class="line">    ASSERT( (pthread-&gt;status == TASK_BLOCKED) || (pthread-&gt;status == TASK_WAITNG) ||(pthread-&gt;status == TASK_HANGING) );</span><br><span class="line">    <span class="keyword">if</span>(pthread-&gt;status != TASK_READY)</span><br><span class="line">    {</span><br><span class="line">        ASSERT(!elem_find(&amp;thread_ready_list,&amp;pthread-&gt;general_tag));</span><br><span class="line">        <span class="keyword">if</span> (elem_find(&amp;thread_ready_list, &amp;pthread-&gt;general_tag)) </span><br><span class="line">        { </span><br><span class="line">            PANIC(<span class="string">"thread_unblock: blocked thread in ready_list\n"</span>); </span><br><span class="line">        } </span><br><span class="line">        list_push(&amp;thread_ready_list, &amp;pthread-&gt;general_tag); <span class="comment">//放到队列的最前面，使其尽快得到调度</span></span><br><span class="line">        pthread-&gt;status = TASK_READY; </span><br><span class="line">    } </span><br><span class="line">    intr_set_status(old_status);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//当前线程阻塞，并且标志其状态为stat</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_block</span><span class="params">(<span class="keyword">enum</span> task_status stat)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//解除pthread线程的阻塞状态</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">thread_unblock</span><span class="params">(<span class="keyword">struct</span> task_struct* pthread)</span>;</span><br></pre></td></tr></table></figure></div><hr><h4 id="G1-3-锁的实现"><a href="#G1-3-锁的实现" class="headerlink" title="G1.3 锁的实现"></a>G1.3 锁的实现</h4><p>下面来实现锁，要添加两个文件，在<code>thread</code>文件夹下添加<code>sync.c</code> <code>stnc.h</code>，实现一个<strong>简单的信号量</strong>(只有0和1)</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/sync.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化信号量</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sema_init</span><span class="params">(<span class="keyword">struct</span> semaphore* psema,<span class="type">uint8_t</span> value)</span></span><br><span class="line">{</span><br><span class="line">    psema-&gt;value = value;       <span class="comment">//为信号量赋初值</span></span><br><span class="line">    list_init(&amp;psema-&gt;waiters); <span class="comment">//初始化信号量的等待队列</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化锁</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">lock_init</span><span class="params">(<span class="keyword">struct</span> lock* plock)</span></span><br><span class="line">{</span><br><span class="line">    plock-&gt;holder = <span class="literal">NULL</span>;           <span class="comment">//初始为空</span></span><br><span class="line">    plock-&gt;holder_repeat_nr = <span class="number">0</span>;</span><br><span class="line">    sema_init(&amp;plock-&gt;semaphore,<span class="number">1</span>); <span class="comment">//信号量初始值为1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//这里注意我们的信号量操作的值只存在0和1两种选项</span></span><br><span class="line"><span class="comment">//信号量down操作</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sema_down</span><span class="params">(<span class="keyword">struct</span> semaphore* psema)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();   <span class="comment">//保证原子操作</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(psema-&gt;value == <span class="number">0</span>)    <span class="comment">//若为0，表示已经被别人持有</span></span><br><span class="line">    {</span><br><span class="line">        <span class="comment">//当前线程不应该已在信号量的 waiters 队列中</span></span><br><span class="line">        ASSERT(!elem_find(&amp;psema-&gt;waiters,&amp;running_thread()-&gt;general_tag)); </span><br><span class="line">        <span class="keyword">if</span> (elem_find(&amp;psema-&gt;waiters, &amp;running_thread()-&gt;general_tag)) </span><br><span class="line">        { </span><br><span class="line">            PANIC(<span class="string">"sema_down: thread blocked has been in waiters_list\n"</span>); </span><br><span class="line">        } </span><br><span class="line">        <span class="comment">//若信号量的值等于 0，则当前线程把自己加入该锁的等待队列，并且阻塞该队列</span></span><br><span class="line">        list_append(&amp;psema-&gt;waiters, &amp;running_thread()-&gt;general_tag); </span><br><span class="line">        thread_block(TASK_BLOCKED); <span class="comment">// 阻塞线程，直到被唤醒</span></span><br><span class="line">    } </span><br><span class="line">    psema-&gt;value--; <span class="comment">//若不是0，则下面的操作，也就是获得锁</span></span><br><span class="line">    ASSERT(psema-&gt;value == <span class="number">0</span>);</span><br><span class="line">    intr_set_status(old_status);                    <span class="comment">//恢复中断</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//信号量的up操作</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sema_up</span><span class="params">(<span class="keyword">struct</span> semaphore* psema)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();</span><br><span class="line">    ASSERT(psema-&gt;value == <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!list_empty(&amp;psema-&gt;waiters)) <span class="comment">//等待队列非空</span></span><br><span class="line">    { </span><br><span class="line">        <span class="comment">//获取等待队列的PCB</span></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">thread_blocked</span> =</span> elem2entry(<span class="keyword">struct</span> task_struct, general_tag, list_pop(&amp;psema-&gt;waiters)); </span><br><span class="line">        thread_unblock(thread_blocked);     <span class="comment">//释放锁</span></span><br><span class="line">    }</span><br><span class="line">    psema-&gt;value++;</span><br><span class="line">    ASSERT(psema-&gt;value == <span class="number">1</span>);</span><br><span class="line">    intr_set_status(old_status);                    <span class="comment">//恢复中断</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//获取锁 plock</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">lock_acquire</span><span class="params">(<span class="keyword">struct</span> lock* plock)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//排除曾经自己已经持有锁，但是还没有释放的情况</span></span><br><span class="line">    <span class="keyword">if</span>(plock-&gt;holder != running_thread())</span><br><span class="line">    {</span><br><span class="line">        sema_down(&amp;plock-&gt;semaphore);       <span class="comment">//对信号量down操作（获取信号量）</span></span><br><span class="line">        plock-&gt;holder = running_thread();</span><br><span class="line">        ASSERT(plock-&gt;holder_repeat_nr == <span class="number">0</span>); </span><br><span class="line">        plock-&gt;holder_repeat_nr = <span class="number">1</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        plock-&gt;holder_repeat_nr++;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//释放锁 plock</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">lock_release</span><span class="params">(<span class="keyword">struct</span> lock* plock)</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(plock-&gt;holder == running_thread());</span><br><span class="line">    <span class="keyword">if</span>(plock-&gt;holder_repeat_nr &gt;<span class="number">1</span>)  <span class="comment">//自己获取自己锁</span></span><br><span class="line">    {</span><br><span class="line">        plock-&gt;holder_repeat_nr--;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line">    ASSERT(plock-&gt;holder_repeat_nr == <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    plock-&gt;holder = <span class="literal">NULL</span>;   <span class="comment">//持有者置空放在v操作之前</span></span><br><span class="line">    plock-&gt;holder_repeat_nr = <span class="number">0</span>;</span><br><span class="line">    sema_up(&amp;plock-&gt;semaphore); <span class="comment">//释放锁对应的信号量</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/sync.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __THREAD_SYNC_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __THREAD_SYNC_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//信号量结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">semaphore</span>{</span></span><br><span class="line">    <span class="type">uint8_t</span> value;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">waiters</span>;</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//锁结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">lock</span>{</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">holder</span>;</span>     <span class="comment">//锁的持有者</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">semaphore</span> <span class="title">semaphore</span>;</span>     <span class="comment">//用二元信号量实现锁</span></span><br><span class="line">    <span class="type">uint32_t</span> holder_repeat_nr;       <span class="comment">//锁的持有者重复申请锁的次数</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/thread/sync.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化信号量</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sema_init</span><span class="params">(<span class="keyword">struct</span> semaphore* psema,<span class="type">uint8_t</span> value)</span>;</span><br><span class="line"><span class="comment">//初始化锁</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">lock_init</span><span class="params">(<span class="keyword">struct</span> lock* plock)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//这里注意我们的信号量操作的值只存在0和1两种选项</span></span><br><span class="line"><span class="comment">//信号量down操作</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sema_down</span><span class="params">(<span class="keyword">struct</span> semaphore* psema)</span>;</span><br><span class="line"><span class="comment">//信号量的up操作</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sema_up</span><span class="params">(<span class="keyword">struct</span> semaphore* psema)</span>;</span><br><span class="line"><span class="comment">//获取锁 plock</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">lock_acquire</span><span class="params">(<span class="keyword">struct</span> lock* plock)</span>;</span><br><span class="line"><span class="comment">//释放锁 plock</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">lock_release</span><span class="params">(<span class="keyword">struct</span> lock* plock)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><h3 id="G-2-用锁实现终端输出"><a href="#G-2-用锁实现终端输出" class="headerlink" title="G.2 用锁实现终端输出"></a>G.2 用锁实现终端输出</h3><p>这里写的终端，它不是真正意义上的终端，甚至连伪终端都算不上，我们只是通过它让输出变得更整洁。您看到了，它就是对各种锁操作的封装，完全就是锁的应用</p><p>我们将它写在<code>device</code>文件夹下添加<code>console.h</code> <code>console.c</code></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/console.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">console_lock</span>;</span>        <span class="comment">//控制台锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化终端</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    lock_init(&amp;console_lock);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//获取终端</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_acquire</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    lock_acquire(&amp;console_lock);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//释放终端</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_release</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    lock_release(&amp;console_lock);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//终端输出字符串</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_put_str</span><span class="params">(<span class="type">char</span>* str)</span></span><br><span class="line">{</span><br><span class="line">    console_acquire();</span><br><span class="line">    put_str(str);</span><br><span class="line">    console_release();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//终端输出字符</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_put_char</span><span class="params">(<span class="type">uint8_t</span> char_asci)</span></span><br><span class="line">{</span><br><span class="line">    console_acquire();</span><br><span class="line">    put_char(char_asci);</span><br><span class="line">    console_release();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//终端输出十六进制整数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_put_int</span><span class="params">(<span class="type">uint32_t</span> num)</span></span><br><span class="line">{</span><br><span class="line">    console_acquire();</span><br><span class="line">    put_int(num);</span><br><span class="line">    console_release();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/console.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __DEVICE_CONSOLE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __DEVICE_CONSOLE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化终端</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_init</span><span class="params">()</span>;</span><br><span class="line"><span class="comment">//获取终端</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_acquire</span><span class="params">()</span>;</span><br><span class="line"><span class="comment">//释放终端</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_release</span><span class="params">()</span>;</span><br><span class="line"><span class="comment">//终端输出字符串</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_put_str</span><span class="params">(<span class="type">char</span>* str)</span>;</span><br><span class="line"><span class="comment">//终端输出字符</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_put_char</span><span class="params">(<span class="type">uint8_t</span> char_asci)</span>;</span><br><span class="line"><span class="comment">//终端输出十六进制整数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">console_put_int</span><span class="params">(<span class="type">uint32_t</span> num)</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>最后我们添加主函数，将原本的<code>put_str</code> 改成 <code>console_put_str</code>,验证结果:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有</span></span><br><span class="line">    <span class="comment">//创建一个线程，线程名，优先级，线程函数名，参数</span></span><br><span class="line">    thread_start(<span class="string">"k_thread_one"</span>,<span class="number">31</span>,k_thread_one,<span class="string">"AA "</span>);   </span><br><span class="line">    thread_start(<span class="string">"k_thread_two"</span>,<span class="number">8</span>,k_thread_two,<span class="string">"BBBB "</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// intr_disable();         //关中断</span></span><br><span class="line">    intr_enable();      <span class="comment">//打开中断，使时钟中断起作用</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(<span class="string">"MMMM "</span>);</span><br><span class="line">    }   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(arg);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(arg);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最后还是编译写入运行，会发现不会发生报错，并且运行的顺序不再是固定的，而是”随机”的，符合任务阻塞的现象，并且更改字符串长度后依旧可以正常运行，以后我们的输出就使用这个新的函数</p><p>ok呀，终端到此结束，虽然书中表示不会更新，但说不定会找点别的更新进去，因为后面打算看看linux的其它版本的源码来继续完善这个简单的操作系统</p><hr><h2 id="H-输入的实现"><a href="#H-输入的实现" class="headerlink" title="H 输入的实现"></a>H 输入的实现</h2><p>既然前面实现了终端的输出，那么肯定少不了输入，这里开始实现输入，即从键盘中获取输入的字符</p><h3 id="H-1-原理简介"><a href="#H-1-原理简介" class="headerlink" title="H.1 原理简介"></a>H.1 原理简介</h3><blockquote><p>键盘是个独立的设备，在它内部有个叫作键盘编码器的芯片，通常是 Intel 8048 或兼容芯片，它的作用是：每当键盘上发生按键操作，它就向键盘控制器报告哪个键被按下，按键是否弹起。</p></blockquote><blockquote><p>这个键盘控制器可并不在键盘内部，它在主机内部的主板上，通常是 Intel 8042 或兼容芯片，它的作用是接收来自键盘编码器的按键信息，将其解码后保存，然后向中断代理发中断，之后处理器执行相应的中断处理程序读入 8042 处理保存过的按键信息</p></blockquote><blockquote><p>无论是按下键，或是松开键，当键的状态改变后，键盘中的 8048 芯片把按键对应的扫描码（通码或断码）发送到主板上的 8042 芯片，由 8042 处理后保存在自己的寄存器中，然后向 8259A 发送中断信号，这样处理器便去执行键盘中断处理程序，将 8042 处理过的扫描码从它的寄存器中读取出来，继续进行下一步处理</p></blockquote><p>这个中断处理程序就是需要我们编写的，<strong>我们只能得到键的扫描码</strong>，并不会得到ASCII码，然后我们可以将他们俩转化，然后再将它交给<code>put_char</code>函数即可</p><p>当然书中还介绍了键盘扫描码的分类，这里就不多说了，我们直接来进行实战环节即可，因为这个也算是科普了</p><hr><h3 id="H-2-测试中断程序"><a href="#H-2-测试中断程序" class="headerlink" title="H.2 测试中断程序"></a>H.2 测试中断程序</h3><p>在测试键盘的中断程序之前，我们先来添加优化一下之前写的代码，首先是”kernel.S”文件中，添加新的中断，因为我们之前只写到了<code>0x20</code>,同时修改宏<code>IDT_DESC_CNT</code>,因为我们的总数发生了变化</p><p>其实是为了方便演示，所以我们要暂时关闭掉时钟的中断，只打开键盘的中断，所以我们现在还要写8259A的中断屏蔽寄存器（<strong>pic_init()函数</strong>）</p><p>下面开始操作:</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel.S</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 略..............</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;这里将中断都指向对应的函数[idt_table + %1*4]</span></span><br><span class="line"><span class="comment">;第 1 个参数 0x1e 是中断向量号,第二个参数主要是用来占位，表示是否自动压入错误码(ERROR_CODE)</span></span><br><span class="line">VECTOR <span class="number">0X00</span>,<span class="meta">ZERO</span>    <span class="comment">; 除法错误异常</span></span><br><span class="line">VECTOR <span class="number">0x01</span>,<span class="meta">ZERO</span>    <span class="comment">; 调试异常</span></span><br><span class="line">VECTOR <span class="number">0x02</span>,<span class="meta">ZERO</span>    <span class="comment">; 非屏蔽中断 (NMI)</span></span><br><span class="line">VECTOR <span class="number">0x03</span>,<span class="meta">ZERO</span>    <span class="comment">; 断点 (INT3)</span></span><br><span class="line">VECTOR <span class="number">0x04</span>,<span class="meta">ZERO</span>    <span class="comment">; 溢出 (INTO)</span></span><br><span class="line">VECTOR <span class="number">0x05</span>,<span class="meta">ZERO</span>    <span class="comment">; 边界范围超出 (BOUND)</span></span><br><span class="line">VECTOR <span class="number">0x06</span>,<span class="meta">ZERO</span>    <span class="comment">; 无效操作码 (UD2)</span></span><br><span class="line">VECTOR <span class="number">0x07</span>,<span class="meta">ZERO</span>    <span class="comment">; 设备不可用</span></span><br><span class="line">VECTOR <span class="number">0x08</span>,ERROR_CODE <span class="comment">; 双重错误</span></span><br><span class="line">VECTOR <span class="number">0x09</span>,<span class="meta">ZERO</span>    <span class="comment">; 协处理器段超限</span></span><br><span class="line">VECTOR <span class="number">0x0a</span>,ERROR_CODE <span class="comment">; 无效TSS</span></span><br><span class="line">VECTOR <span class="number">0x0b</span>,ERROR_CODE <span class="comment">; 段不存在</span></span><br><span class="line">VECTOR <span class="number">0x0c</span>,<span class="meta">ZERO</span>    <span class="comment">; 栈段错误</span></span><br><span class="line">VECTOR <span class="number">0x0d</span>,ERROR_CODE <span class="comment">; 通用保护错误</span></span><br><span class="line">VECTOR <span class="number">0x0e</span>,ERROR_CODE <span class="comment">; 页错误</span></span><br><span class="line">VECTOR <span class="number">0x0f</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x10</span>,<span class="meta">ZERO</span>    <span class="comment">; x87浮点异常</span></span><br><span class="line">VECTOR <span class="number">0x11</span>,ERROR_CODE <span class="comment">; 对齐检查</span></span><br><span class="line">VECTOR <span class="number">0x12</span>,<span class="meta">ZERO</span>    <span class="comment">; 机器检查</span></span><br><span class="line">VECTOR <span class="number">0x13</span>,<span class="meta">ZERO</span>    <span class="comment">; SIMD浮点异常</span></span><br><span class="line">VECTOR <span class="number">0x14</span>,<span class="meta">ZERO</span>    <span class="comment">; 虚拟化异常</span></span><br><span class="line">VECTOR <span class="number">0x15</span>,<span class="meta">ZERO</span>    <span class="comment">; 控制保护异常</span></span><br><span class="line">VECTOR <span class="number">0x16</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x17</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x18</span>,ERROR_CODE <span class="comment">; 超线程技术异常</span></span><br><span class="line">VECTOR <span class="number">0x19</span>,<span class="meta">ZERO</span>    <span class="comment">; VMM通信异常</span></span><br><span class="line">VECTOR <span class="number">0x1a</span>,ERROR_CODE <span class="comment">; 安全异常</span></span><br><span class="line">VECTOR <span class="number">0x1b</span>,ERROR_CODE <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x1c</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x1d</span>,ERROR_CODE <span class="comment">; 保护模式异常</span></span><br><span class="line">VECTOR <span class="number">0x1e</span>,ERROR_CODE <span class="comment">; 安全启动异常</span></span><br><span class="line">VECTOR <span class="number">0x1f</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x20</span>,<span class="meta">ZERO</span>    <span class="comment">; 通常用于系统调用或定时器中断，这里用来测试中断</span></span><br><span class="line">VECTOR <span class="number">0x21</span>,<span class="meta">ZERO</span> <span class="comment">;键盘中断对应的入口</span></span><br><span class="line">VECTOR <span class="number">0x22</span>,<span class="meta">ZERO</span> <span class="comment">;级联用的</span></span><br><span class="line">VECTOR <span class="number">0x23</span>,<span class="meta">ZERO</span> <span class="comment">;串口 2 对应的入口</span></span><br><span class="line">VECTOR <span class="number">0x24</span>,<span class="meta">ZERO</span> <span class="comment">;串口 1 对应的入口</span></span><br><span class="line">VECTOR <span class="number">0x25</span>,<span class="meta">ZERO</span> <span class="comment">;并口 2 对应的入口</span></span><br><span class="line">VECTOR <span class="number">0x26</span>,<span class="meta">ZERO</span> <span class="comment">;软盘对应的入口</span></span><br><span class="line">VECTOR <span class="number">0x27</span>,<span class="meta">ZERO</span> <span class="comment">;并口 1 对应的入口</span></span><br><span class="line">VECTOR <span class="number">0x28</span>,<span class="meta">ZERO</span> <span class="comment">;实时时钟对应的入口</span></span><br><span class="line">VECTOR <span class="number">0x29</span>,<span class="meta">ZERO</span> <span class="comment">;重定向</span></span><br><span class="line">VECTOR <span class="number">0x2a</span>,<span class="meta">ZERO</span> <span class="comment">;保留</span></span><br><span class="line">VECTOR <span class="number">0x2b</span>,<span class="meta">ZERO</span> <span class="comment">;保留</span></span><br><span class="line">VECTOR <span class="number">0x2c</span>,<span class="meta">ZERO</span> <span class="comment">;ps/2 鼠标</span></span><br><span class="line">VECTOR <span class="number">0x2d</span>,<span class="meta">ZERO</span> <span class="comment">;fpu 浮点单元异常</span></span><br><span class="line">VECTOR <span class="number">0x2e</span>,<span class="meta">ZERO</span> <span class="comment">;硬盘</span></span><br><span class="line">VECTOR <span class="number">0x2f</span>,<span class="meta">ZERO</span> <span class="comment">;保留</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span>              <span class="comment">//put_str</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span>                 <span class="comment">//io操作,联合汇编</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_CNT 0x33       <span class="comment">// 目前总共支持的中断数,这里写的偏大一点</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_CTRL 0x20         <span class="comment">// 主片的控制端口是 0x20 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_DATA 0x21         <span class="comment">// 主片的数据端口是 0x21 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_CTRL 0xa0         <span class="comment">// 从片的控制端口是 0xa0 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_DATA 0xa1         <span class="comment">// 从片的数据端口是 0xa1</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EFLAGS_IF   0x00000200       <span class="comment">// eflags寄存器中的if位为1</span></span></span><br><span class="line"><span class="comment">//获取 eflags 寄存器的值,EFLAG_VAR 是返回值</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GET_EFLAGS(EFLAG_VAR) asm volatile(<span class="string">"pushfl; popl %0"</span> : <span class="string">"=g"</span> (EFLAG_VAR))</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 中断门描述符结构体</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span>  <span class="title">gate_desc</span>{</span></span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_low_word;</span><br><span class="line">    <span class="type">uint16_t</span>    selector;</span><br><span class="line">    <span class="type">uint8_t</span>     dcount;     <span class="comment">//此项为双字计数字段,是门描述符中的第4字节。为固定值,不用考虑</span></span><br><span class="line">    <span class="type">uint8_t</span>     attribute;</span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_high_word;     </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态函数声明,非必须</span></span><br><span class="line"><span class="comment">// intr_handler 是自己定义的(interrupt.h) typedef void* intr_handler；</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span>;</span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">gate_desc</span> <span class="title">idt</span>[<span class="title">IDT_DESC_CNT</span>];</span>              <span class="comment">// idt是中断描述符,本质上一个中断门描述符数组</span></span><br><span class="line"><span class="keyword">extern</span> intr_handler intr_entry_table[IDT_DESC_CNT];     <span class="comment">// 声明引用定义kernel.S中的中断处理函数入口函数 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">//添加部分:</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exception_init</span><span class="params">(<span class="type">void</span>)</span>;                   <span class="comment">//完成一般中断处理函数注册及异常名称注册</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">general_intr_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span>;   <span class="comment">//通用的中断处理函数,一般在异常出现时处理</span></span><br><span class="line"><span class="type">const</span> <span class="type">char</span>* intr_name[IDT_DESC_CNT];                <span class="comment">//用于保留异常的名字</span></span><br><span class="line">intr_handler idt_table[IDT_DESC_CNT];               <span class="comment">//中断处理函数程序数组,在kernel.S 中定义的 intrXXentry是中断处理函数的入口,最终调用 idt_table 里面的函数</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化可编程中断控制器 8259A */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">pic_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/*初始化主片 */</span> </span><br><span class="line">    outb (PIC_M_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x20</span>); <span class="comment">// ICW2: 起始中断向量号为 0x20 </span></span><br><span class="line">    <span class="comment">//也就是 IR[0-7] 为 0x20 ~ 0x27 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x04</span>); <span class="comment">// ICW3: IR2 接从片</span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line">    <span class="comment">/*初始化从片 */</span> </span><br><span class="line">    outb (PIC_S_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x28</span>); <span class="comment">// ICW2: 起始中断向量号为 0x28 </span></span><br><span class="line">    <span class="comment">// 也就是 IR[8-15]为 0x28 ~ 0x2F </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x02</span>); <span class="comment">// ICW3: 设置从片连接到主片的 IR2 引脚</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//outb (PIC_M_DATA, 0xfe);      //接受时钟产生的中断</span></span><br><span class="line">    outb(PIC_S_DATA,<span class="number">0xfd</span>);          <span class="comment">//接收键盘中断          </span></span><br><span class="line">    outb(PIC_S_DATA, <span class="number">0xff</span>);         <span class="comment">//其余全部关闭</span></span><br><span class="line">    put_str(<span class="string">" pic_init done\n"</span>); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//略 ....................</span></span><br></pre></td></tr></table></figure></div><p>然后来开始写键盘的测试驱动，放在<code>device</code>文件夹下，创建<code>keyboard.c/h</code>文件</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/keyboard.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> KBD_BUF_PORT 0x60   <span class="comment">//键盘buffer寄存器的端口号:0x60</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//键盘中断处理函数</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">intr_keyboard_handler</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    put_char(<span class="string">'k'</span>);</span><br><span class="line">    <span class="comment">//必须要读取输出缓冲区寄存器，否则8042不再继续响应键盘中断</span></span><br><span class="line">    <span class="type">uint8_t</span> scancode = inb(KBD_BUF_PORT);</span><br><span class="line">    put_int(scancode);  <span class="comment">//输出一下看看</span></span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//键盘初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">keyboard_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"Keyboard init start\n"</span>);</span><br><span class="line">    register_handler(<span class="number">0x21</span>,intr_keyboard_handler);</span><br><span class="line">    put_str(<span class="string">"Keyboard init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/keyboard.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __DEVICE_KEYBOARD_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __DEVICE_KEYBOARD_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//键盘中断处理函数</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">intr_keyboard_handler</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="comment">//键盘初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">keyboard_init</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有</span></span><br><span class="line">    <span class="comment">//创建一个线程，线程名，优先级，线程函数名，参数</span></span><br><span class="line">    <span class="comment">// thread_start("k_thread_one",31,k_thread_one,"AA ");   </span></span><br><span class="line">    <span class="comment">// thread_start("k_thread_two",8,k_thread_two,"BBBB ");</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// intr_disable();         //关中断</span></span><br><span class="line">    intr_enable();      <span class="comment">//打开中断，使时钟中断起作用</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">// console_put_str("MMMM ");</span></span><br><span class="line">    }   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(arg);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        console_put_str(arg);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>修改完之后主要还要修改对应的Makefile文件，添加编译的c文件，主函数中取消对多任务的注册，写成死循环即可</p><p>然后编译写入运行，对着终端按下键盘，就会发现屏幕上有输出(扫描码)</p><hr><h3 id="H-3-编写键盘驱动"><a href="#H-3-编写键盘驱动" class="headerlink" title="H.3 编写键盘驱动"></a>H.3 编写键盘驱动</h3><p>下面的驱动主要通过扫描码到ASCII码的转化来实现，来编写一个映射表实现键盘的读入</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/keyboard.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"keyboard.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> KBD_BUF_PORT 0x60 <span class="comment">// 键盘buffer寄存器端口号为0x60</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 用转义字符定义部分控制字符 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> esc<span class="string">'\033'</span> <span class="comment">// 八进制表示字符,也可以用十六进制'\x1b'</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> backspace<span class="string">'\b'</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> tab<span class="string">'\t'</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> enter<span class="string">'\r'</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> delete<span class="string">'\177'</span> <span class="comment">// 八进制表示字符,十六进制为'\x7f'</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 以上不可见字符一律定义为0 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> char_invisible0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ctrl_l_charchar_invisible</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ctrl_r_charchar_invisible</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> shift_l_charchar_invisible</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> shift_r_charchar_invisible</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> alt_l_charchar_invisible</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> alt_r_charchar_invisible</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> caps_lock_charchar_invisible</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 定义控制字符的通码和断码 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> shift_l_make0x2a</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> shift_r_make 0x36 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> alt_l_make   0x38</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> alt_r_make   0xe038</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> alt_r_break   0xe0b8</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ctrl_l_make  0x1d</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ctrl_r_make  0xe01d</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ctrl_r_break 0xe09d</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> caps_lock_make 0x3a</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 定义以下变量记录相应键是否按下的状态,</span></span><br><span class="line"><span class="comment"> * ext_scancode用于记录makecode是否以0xe0开头 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">bool</span> ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 以通码make_code为索引的二维数组 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">char</span> keymap[][<span class="number">2</span>] = {</span><br><span class="line"><span class="comment">/* 扫描码   未与shift组合  与shift组合*/</span></span><br><span class="line"><span class="comment">/* ---------------------------------- */</span></span><br><span class="line"><span class="comment">/* 0x00 */</span>{<span class="number">0</span>,<span class="number">0</span>},</span><br><span class="line"><span class="comment">/* 0x01 */</span>{esc,esc},</span><br><span class="line"><span class="comment">/* 0x02 */</span>{<span class="string">'1'</span>,<span class="string">'!'</span>},</span><br><span class="line"><span class="comment">/* 0x03 */</span>{<span class="string">'2'</span>,<span class="string">'@'</span>},</span><br><span class="line"><span class="comment">/* 0x04 */</span>{<span class="string">'3'</span>,<span class="string">'#'</span>},</span><br><span class="line"><span class="comment">/* 0x05 */</span>{<span class="string">'4'</span>,<span class="string">'$'</span>},</span><br><span class="line"><span class="comment">/* 0x06 */</span>{<span class="string">'5'</span>,<span class="string">'%'</span>},</span><br><span class="line"><span class="comment">/* 0x07 */</span>{<span class="string">'6'</span>,<span class="string">'^'</span>},</span><br><span class="line"><span class="comment">/* 0x08 */</span>{<span class="string">'7'</span>,<span class="string">'&amp;'</span>},</span><br><span class="line"><span class="comment">/* 0x09 */</span>{<span class="string">'8'</span>,<span class="string">'*'</span>},</span><br><span class="line"><span class="comment">/* 0x0A */</span>{<span class="string">'9'</span>,<span class="string">'('</span>},</span><br><span class="line"><span class="comment">/* 0x0B */</span>{<span class="string">'0'</span>,<span class="string">')'</span>},</span><br><span class="line"><span class="comment">/* 0x0C */</span>{<span class="string">'-'</span>,<span class="string">'_'</span>},</span><br><span class="line"><span class="comment">/* 0x0D */</span>{<span class="string">'='</span>,<span class="string">'+'</span>},</span><br><span class="line"><span class="comment">/* 0x0E */</span>{backspace, backspace},</span><br><span class="line"><span class="comment">/* 0x0F */</span>{tab,tab},</span><br><span class="line"><span class="comment">/* 0x10 */</span>{<span class="string">'q'</span>,<span class="string">'Q'</span>},</span><br><span class="line"><span class="comment">/* 0x11 */</span>{<span class="string">'w'</span>,<span class="string">'W'</span>},</span><br><span class="line"><span class="comment">/* 0x12 */</span>{<span class="string">'e'</span>,<span class="string">'E'</span>},</span><br><span class="line"><span class="comment">/* 0x13 */</span>{<span class="string">'r'</span>,<span class="string">'R'</span>},</span><br><span class="line"><span class="comment">/* 0x14 */</span>{<span class="string">'t'</span>,<span class="string">'T'</span>},</span><br><span class="line"><span class="comment">/* 0x15 */</span>{<span class="string">'y'</span>,<span class="string">'Y'</span>},</span><br><span class="line"><span class="comment">/* 0x16 */</span>{<span class="string">'u'</span>,<span class="string">'U'</span>},</span><br><span class="line"><span class="comment">/* 0x17 */</span>{<span class="string">'i'</span>,<span class="string">'I'</span>},</span><br><span class="line"><span class="comment">/* 0x18 */</span>{<span class="string">'o'</span>,<span class="string">'O'</span>},</span><br><span class="line"><span class="comment">/* 0x19 */</span>{<span class="string">'p'</span>,<span class="string">'P'</span>},</span><br><span class="line"><span class="comment">/* 0x1A */</span>{<span class="string">'['</span>,<span class="string">'{'</span>},</span><br><span class="line"><span class="comment">/* 0x1B */</span>{<span class="string">']'</span>,<span class="string">'}'</span>},</span><br><span class="line"><span class="comment">/* 0x1C */</span>{enter,  enter},</span><br><span class="line"><span class="comment">/* 0x1D */</span>{ctrl_l_char, ctrl_l_char},</span><br><span class="line"><span class="comment">/* 0x1E */</span>{<span class="string">'a'</span>,<span class="string">'A'</span>},</span><br><span class="line"><span class="comment">/* 0x1F */</span>{<span class="string">'s'</span>,<span class="string">'S'</span>},</span><br><span class="line"><span class="comment">/* 0x20 */</span>{<span class="string">'d'</span>,<span class="string">'D'</span>},</span><br><span class="line"><span class="comment">/* 0x21 */</span>{<span class="string">'f'</span>,<span class="string">'F'</span>},</span><br><span class="line"><span class="comment">/* 0x22 */</span>{<span class="string">'g'</span>,<span class="string">'G'</span>},</span><br><span class="line"><span class="comment">/* 0x23 */</span>{<span class="string">'h'</span>,<span class="string">'H'</span>},</span><br><span class="line"><span class="comment">/* 0x24 */</span>{<span class="string">'j'</span>,<span class="string">'J'</span>},</span><br><span class="line"><span class="comment">/* 0x25 */</span>{<span class="string">'k'</span>,<span class="string">'K'</span>},</span><br><span class="line"><span class="comment">/* 0x26 */</span>{<span class="string">'l'</span>,<span class="string">'L'</span>},</span><br><span class="line"><span class="comment">/* 0x27 */</span>{<span class="string">';'</span>,<span class="string">':'</span>},</span><br><span class="line"><span class="comment">/* 0x28 */</span>{<span class="string">'\''</span>,<span class="string">'"'</span>},</span><br><span class="line"><span class="comment">/* 0x29 */</span>{<span class="string">'`'</span>,<span class="string">'~'</span>},</span><br><span class="line"><span class="comment">/* 0x2A */</span>{shift_l_char, shift_l_char},</span><br><span class="line"><span class="comment">/* 0x2B */</span>{<span class="string">'\\'</span>,<span class="string">'|'</span>},</span><br><span class="line"><span class="comment">/* 0x2C */</span>{<span class="string">'z'</span>,<span class="string">'Z'</span>},</span><br><span class="line"><span class="comment">/* 0x2D */</span>{<span class="string">'x'</span>,<span class="string">'X'</span>},</span><br><span class="line"><span class="comment">/* 0x2E */</span>{<span class="string">'c'</span>,<span class="string">'C'</span>},</span><br><span class="line"><span class="comment">/* 0x2F */</span>{<span class="string">'v'</span>,<span class="string">'V'</span>},</span><br><span class="line"><span class="comment">/* 0x30 */</span>{<span class="string">'b'</span>,<span class="string">'B'</span>},</span><br><span class="line"><span class="comment">/* 0x31 */</span>{<span class="string">'n'</span>,<span class="string">'N'</span>},</span><br><span class="line"><span class="comment">/* 0x32 */</span>{<span class="string">'m'</span>,<span class="string">'M'</span>},</span><br><span class="line"><span class="comment">/* 0x33 */</span>{<span class="string">','</span>,<span class="string">'&lt;'</span>},</span><br><span class="line"><span class="comment">/* 0x34 */</span>{<span class="string">'.'</span>,<span class="string">'&gt;'</span>},</span><br><span class="line"><span class="comment">/* 0x35 */</span>{<span class="string">'/'</span>,<span class="string">'?'</span>},</span><br><span class="line"><span class="comment">/* 0x36*/</span>{shift_r_char, shift_r_char},</span><br><span class="line"><span class="comment">/* 0x37 */</span>{<span class="string">'*'</span>,<span class="string">'*'</span>},    </span><br><span class="line"><span class="comment">/* 0x38 */</span>{alt_l_char, alt_l_char},</span><br><span class="line"><span class="comment">/* 0x39 */</span>{<span class="string">' '</span>,<span class="string">' '</span>},</span><br><span class="line"><span class="comment">/* 0x3A */</span>{caps_lock_char, caps_lock_char}</span><br><span class="line"><span class="comment">/*其它按键暂不处理*/</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 键盘中断处理程序 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">intr_keyboard_handler</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 这次中断发生前的上一次中断,以下任意三个键是否有按下 */</span></span><br><span class="line">   <span class="type">bool</span> ctrl_down_last = ctrl_status;  </span><br><span class="line">   <span class="type">bool</span> shift_down_last = shift_status;</span><br><span class="line">   <span class="type">bool</span> caps_lock_last = caps_lock_status;</span><br><span class="line"></span><br><span class="line">   <span class="type">bool</span> break_code;</span><br><span class="line">   <span class="type">uint16_t</span> scancode = inb(KBD_BUF_PORT);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 若扫描码是e0开头的,表示此键的按下将产生多个扫描码,</span></span><br><span class="line"><span class="comment"> * 所以马上结束此次中断处理函数,等待下一个扫描码进来*/</span> </span><br><span class="line">   <span class="keyword">if</span> (scancode == <span class="number">0xe0</span>) { </span><br><span class="line">      ext_scancode = <span class="literal">true</span>;    <span class="comment">// 打开e0标记</span></span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   }</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 如果上次是以0xe0开头,将扫描码合并 */</span></span><br><span class="line">   <span class="keyword">if</span> (ext_scancode) {</span><br><span class="line">      scancode = ((<span class="number">0xe000</span>) | scancode);</span><br><span class="line">      ext_scancode = <span class="literal">false</span>;   <span class="comment">// 关闭e0标记</span></span><br><span class="line">   }   </span><br><span class="line"></span><br><span class="line">   break_code = ((scancode &amp; <span class="number">0x0080</span>) != <span class="number">0</span>);   <span class="comment">// 获取break_code</span></span><br><span class="line">   </span><br><span class="line">   <span class="keyword">if</span> (break_code) {   <span class="comment">// 若是断码break_code(按键弹起时产生的扫描码)</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 由于ctrl_r 和alt_r的make_code和break_code都是两字节,</span></span><br><span class="line"><span class="comment">   所以可用下面的方法取make_code,多字节的扫描码暂不处理 */</span></span><br><span class="line">      <span class="type">uint16_t</span> make_code = (scancode &amp;= <span class="number">0xff7f</span>);   <span class="comment">// 得到其make_code(按键按下时产生的扫描码)</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 若是任意以下三个键弹起了,将状态置为false */</span></span><br><span class="line">      <span class="keyword">if</span> (make_code == ctrl_l_make || make_code == ctrl_r_make) {</span><br><span class="line"> ctrl_status = <span class="literal">false</span>;</span><br><span class="line">      } <span class="keyword">else</span> <span class="keyword">if</span> (make_code == shift_l_make || make_code == shift_r_make) {</span><br><span class="line"> shift_status = <span class="literal">false</span>;</span><br><span class="line">      } <span class="keyword">else</span> <span class="keyword">if</span> (make_code == alt_l_make || make_code == alt_r_make) {</span><br><span class="line"> alt_status = <span class="literal">false</span>;</span><br><span class="line">      } <span class="comment">/* 由于caps_lock不是弹起后关闭,所以需要单独处理 */</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span>;   <span class="comment">// 直接返回结束此次中断处理程序</span></span><br><span class="line"></span><br><span class="line">   } </span><br><span class="line">   <span class="comment">/* 若为通码,只处理数组中定义的键以及alt_right和ctrl键,全是make_code */</span></span><br><span class="line">   <span class="keyword">else</span> <span class="keyword">if</span> ((scancode &gt; <span class="number">0x00</span> &amp;&amp; scancode &lt; <span class="number">0x3b</span>) || \</span><br><span class="line">       (scancode == alt_r_make) || \</span><br><span class="line">       (scancode == ctrl_r_make)) {</span><br><span class="line">      <span class="type">bool</span> shift = <span class="literal">false</span>;  <span class="comment">// 判断是否与shift组合,用来在一维数组中索引对应的字符</span></span><br><span class="line">      <span class="keyword">if</span> ((scancode &lt; <span class="number">0x0e</span>) || (scancode == <span class="number">0x29</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x1a</span>) || (scancode == <span class="number">0x1b</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x2b</span>) || (scancode == <span class="number">0x27</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x28</span>) || (scancode == <span class="number">0x33</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x34</span>) || (scancode == <span class="number">0x35</span>)) {  </span><br><span class="line">    <span class="comment">/****** 代表两个字母的键 ********</span></span><br><span class="line"><span class="comment">     0x0e 数字'0'~'9',字符'-',字符'='</span></span><br><span class="line"><span class="comment">     0x29 字符'`'</span></span><br><span class="line"><span class="comment">     0x1a 字符'['</span></span><br><span class="line"><span class="comment">     0x1b 字符']'</span></span><br><span class="line"><span class="comment">     0x2b 字符'\\'</span></span><br><span class="line"><span class="comment">     0x27 字符';'</span></span><br><span class="line"><span class="comment">     0x28 字符'\''</span></span><br><span class="line"><span class="comment">     0x33 字符','</span></span><br><span class="line"><span class="comment">     0x34 字符'.'</span></span><br><span class="line"><span class="comment">     0x35 字符'/' </span></span><br><span class="line"><span class="comment">    *******************************/</span></span><br><span class="line"> <span class="keyword">if</span> (shift_down_last) {  <span class="comment">// 如果同时按下了shift键</span></span><br><span class="line">    shift = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">      } <span class="keyword">else</span> {  <span class="comment">// 默认为字母键</span></span><br><span class="line"> <span class="keyword">if</span> (shift_down_last &amp;&amp; caps_lock_last) {  <span class="comment">// 如果shift和capslock同时按下</span></span><br><span class="line">    shift = <span class="literal">false</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (shift_down_last || caps_lock_last) { <span class="comment">// 如果shift和capslock任意被按下</span></span><br><span class="line">    shift = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line">    shift = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">      <span class="type">uint8_t</span> index = (scancode &amp;= <span class="number">0x00ff</span>);  <span class="comment">// 将扫描码的高字节置0,主要是针对高字节是e0的扫描码.</span></span><br><span class="line">      <span class="type">char</span> cur_char = keymap[index][shift];  <span class="comment">// 在数组中找到对应的字符</span></span><br><span class="line">   </span><br><span class="line">      <span class="comment">/* 只处理ascii码不为0的键 */</span></span><br><span class="line">      <span class="keyword">if</span> (cur_char) {</span><br><span class="line"> put_char(cur_char);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 记录本次是否按下了下面几类控制键之一,供下次键入时判断组合键 */</span></span><br><span class="line">      <span class="keyword">if</span> (scancode == ctrl_l_make || scancode == ctrl_r_make) {</span><br><span class="line"> ctrl_status = <span class="literal">true</span>;</span><br><span class="line">      } <span class="keyword">else</span> <span class="keyword">if</span> (scancode == shift_l_make || scancode == shift_r_make) {</span><br><span class="line"> shift_status = <span class="literal">true</span>;</span><br><span class="line">      } <span class="keyword">else</span> <span class="keyword">if</span> (scancode == alt_l_make || scancode == alt_r_make) {</span><br><span class="line"> alt_status = <span class="literal">true</span>;</span><br><span class="line">      } <span class="keyword">else</span> <span class="keyword">if</span> (scancode == caps_lock_make) {</span><br><span class="line">      <span class="comment">/* 不管之前是否有按下caps_lock键,当再次按下时则状态取反,</span></span><br><span class="line"><span class="comment">       * 即:已经开启时,再按下同样的键是关闭。关闭时按下表示开启。*/</span></span><br><span class="line"> caps_lock_status = !caps_lock_status;</span><br><span class="line">      }</span><br><span class="line">   } <span class="keyword">else</span> {</span><br><span class="line">      put_str(<span class="string">"unknown key\n"</span>);</span><br><span class="line">   }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 键盘初始化 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">keyboard_init</span><span class="params">()</span> {</span><br><span class="line">   put_str(<span class="string">"keyboard init start\n"</span>);</span><br><span class="line">   register_handler(<span class="number">0x21</span>, intr_keyboard_handler);</span><br><span class="line">   put_str(<span class="string">"keyboard init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>头文件没有更改，运行后可以实现基本的输入</p><hr><h3 id="H-4-环形输入缓冲区"><a href="#H-4-环形输入缓冲区" class="headerlink" title="H.4 环形输入缓冲区"></a>H.4 环形输入缓冲区</h3><p>但是真正的中断还要实现<strong>输入缓冲区</strong>，因为我们通常在输入一串指令，然后以回车来结束，在结束之前我们就需要找一个缓冲区来存储这些</p><blockquote><p>缓冲区是多个线程共同使用的共享内存，线程在并行访问它时难免会乱套，我们不能指望线程们会老老实实排好队，以串行的方式逐个使用缓冲区。缓冲区大小无关紧要，问题的关键在于缓冲区操作上，因此最好是在缓冲区的操作方法上下功夫，保证对缓冲区是互斥访问，并且不会对其过度使用，从而确保不会使缓冲区遭到破坏。也就是说，只要我们能够设计出合理的缓冲区操作方式，就能够解决生产者与消费者问题</p></blockquote><blockquote><p>对于缓冲区的访问，我们提供两个指针，一个是头指针，用于往缓冲区中写数据，另一个是尾指针，用于从缓冲区中读数据。每次通过头指针往缓冲区中写入一个数据后，使头指针加 1 指向缓冲区中下一个可写入数据的地址，每次通过尾指针从缓冲区中读取一个数据后，使尾指针加 1 指向缓冲区中下一个可读入数据的地址，也就是说，缓冲区相当于一个<strong>队列</strong>，数据在队列头被写入，在队尾处被读出</p></blockquote><p>下面来具体实现一下，创建<code>ioqueue.c/h</code>,也放在device下:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/ioqueue.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ioqueue.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化io队列ioq</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ioqueue_init</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span></span><br><span class="line">{</span><br><span class="line">    lock_init(&amp;ioq-&gt;lock);  <span class="comment">//初始化io队列的锁</span></span><br><span class="line">    ioq-&gt;producer = ioq-&gt;consumer = <span class="literal">NULL</span>;       <span class="comment">//生产者和消费者都置空</span></span><br><span class="line">    ioq-&gt;head = ioq-&gt;tail= <span class="number">0</span>;                   <span class="comment">//队列的首位指针指向缓冲区的数组的第0个位置</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//返回pos在缓冲区的下一个位置值</span></span><br><span class="line"><span class="type">static</span> <span class="type">int32_t</span> <span class="title function_">next_pos</span><span class="params">(<span class="type">int32_t</span> pos)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> (pos+<span class="number">1</span>) % bufsize;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//判断队列是否已经满</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">ioq_full</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line">    <span class="keyword">return</span> (next_pos(ioq-&gt;head) == ioq-&gt;tail);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//判断队列是否为空</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">ioq_empty</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line">    <span class="keyword">return</span> (ioq-&gt;head == ioq-&gt;tail);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//是当前消费者或者生产者在此缓冲区上等待</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">ioq_wait</span><span class="params">(<span class="keyword">struct</span> task_struct** waiter)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//*waiter 相当于 ioq-&gt;producer / ioq-&gt;consumer</span></span><br><span class="line">    ASSERT(*waiter == <span class="literal">NULL</span> &amp;&amp; waiter !== <span class="literal">NULL</span>);</span><br><span class="line">    *waiter == running_thread();</span><br><span class="line">    thread_block(TASK_BLOCKED);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//唤醒waiter</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">wakeup</span><span class="params">(<span class="keyword">struct</span> task_struct** waiter)</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(*waiter == <span class="literal">NULL</span>);</span><br><span class="line">    thread_unblock(*waiter);</span><br><span class="line">    *waiter = <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//消费者从ioq队列获取一个字符</span></span><br><span class="line"><span class="type">char</span> <span class="title function_">ioq_getchar</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span></span><br><span class="line">{</span><br><span class="line">    ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//若缓冲区为空,则把ioq-&gt;consumer(消费者)记为当前线程自己</span></span><br><span class="line">    <span class="comment">//目的是将来生产者往缓冲区里装商品时候，生产者知道该唤醒哪个消费者</span></span><br><span class="line">    <span class="comment">//也就是唤醒当前线程的自己</span></span><br><span class="line">    <span class="keyword">while</span>(ioq_empty(ioq))</span><br><span class="line">    {</span><br><span class="line">        lock_acquire(&amp;ioq-&gt;lock);</span><br><span class="line">        ioq_wait(&amp;ioq-&gt;consumer);</span><br><span class="line">        lock_release(&amp;ioq-&gt;lock);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> byte = ioq-&gt;buf[ioq-&gt;tail];    <span class="comment">//从缓冲区取出</span></span><br><span class="line">    ioq-&gt;tail = next_pos(ioq-&gt;tail);    <span class="comment">//把游标移动到下一个位置</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(ioq-&gt;producer != <span class="literal">NULL</span>)</span><br><span class="line">    {</span><br><span class="line">        wakeup(&amp;ioq-&gt;producer);     <span class="comment">//唤醒生产者</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> byte;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//生产者往 ioq 队列中写入一个字符 byte</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ioq_putchar</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq, <span class="type">char</span> byte)</span> </span><br><span class="line">{ </span><br><span class="line">    ASSERT(intr_get_status() == INTR_OFF); </span><br><span class="line"></span><br><span class="line">    <span class="comment">//若缓冲区（队列）已经满了，把生产者 ioq-&gt;producer 记为自己，</span></span><br><span class="line">    <span class="comment">//为的是当缓冲区里的东西被消费者取完后让消费者知道唤醒哪个生产者，</span></span><br><span class="line">    <span class="comment">//也就是唤醒当前线程自己*/ </span></span><br><span class="line">    <span class="keyword">while</span> (ioq_full(ioq)) </span><br><span class="line">    { </span><br><span class="line">        lock_acquire(&amp;ioq-&gt;lock); </span><br><span class="line">        ioq_wait(&amp;ioq-&gt;producer); </span><br><span class="line">        lock_release(&amp;ioq-&gt;lock); </span><br><span class="line">    } </span><br><span class="line">    ioq-&gt;buf[ioq-&gt;head] = byte; <span class="comment">// 把字节放入缓冲区中</span></span><br><span class="line">    ioq-&gt;head = next_pos(ioq-&gt;head); <span class="comment">// 把写游标移到下一位置</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (ioq-&gt;consumer != <span class="literal">NULL</span>) </span><br><span class="line">    { </span><br><span class="line">        wakeup(&amp;ioq-&gt;consumer); <span class="comment">// 唤醒消费者</span></span><br><span class="line">    } </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/ioqueue.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __DEVICE_IPQUEUE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __DEVICE_IPQUEUE_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> bufsize 64</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//环形队列</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ioqueue</span>{</span></span><br><span class="line">    <span class="comment">//生产者消费者问题</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">lock</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//生产者，如果缓冲区不满就继续往里面放数据，否则就睡眠</span></span><br><span class="line">    <span class="comment">//这里记录哪个生产者在此缓冲区上睡眠</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">producer</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//消费者，如果不空就继续从里面拿数据，否则就睡眠</span></span><br><span class="line">    <span class="comment">//这里记录哪个消费者在此缓冲区上睡眠</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">consumer</span>;</span></span><br><span class="line">    </span><br><span class="line">    <span class="type">char</span> buf[bufsize];      <span class="comment">//缓冲区大小</span></span><br><span class="line">    <span class="type">int32_t</span> head;           <span class="comment">//队首，数据往队首处写入</span></span><br><span class="line">    <span class="type">int32_t</span> tail;           <span class="comment">//队尾，数据从队尾读出</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">ioqueue_init</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span>;</span><br><span class="line"><span class="comment">//返回pos在缓冲区的下一个位置值</span></span><br><span class="line"><span class="type">static</span> <span class="type">int32_t</span> <span class="title function_">next_pos</span><span class="params">(<span class="type">int32_t</span> pos)</span>;</span><br><span class="line"><span class="comment">//判断队列是否已经满</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">ioq_full</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span>;</span><br><span class="line"><span class="comment">//判断队列是否为空</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">ioq_empty</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span>;</span><br><span class="line"><span class="comment">//消费者从ioq队列获取一个字符</span></span><br><span class="line"><span class="type">char</span> <span class="title function_">ioq_getchar</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq)</span>;</span><br><span class="line"><span class="comment">//生产者往 ioq 队列中写入一个字符 byte</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ioq_putchar</span><span class="params">(<span class="keyword">struct</span> ioqueue* ioq, <span class="type">char</span> byte)</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>同时，简单修改一下之前写的键盘的代码，给出主要修改部分</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*略.......*/</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ioqueue</span> <span class="title">kbd_buf</span>;</span>         <span class="comment">//定义环形缓冲区</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*略.......*/</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 只处理ascii码不为0的键 */</span></span><br><span class="line">      <span class="keyword">if</span> (cur_char) </span><br><span class="line">      {</span><br><span class="line">            <span class="comment">//如果cur_char 不为0，则添加到ked_buf中(提前判断缓冲区是否满)</span></span><br><span class="line">            <span class="keyword">if</span>(!ioq_full(&amp;kbd_buf))</span><br><span class="line">            {</span><br><span class="line">                put_char(cur_char);     <span class="comment">//临时的</span></span><br><span class="line">                ioq_putchar(&amp;kbd_buf,cur_char);</span><br><span class="line">            }</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">      }</span><br><span class="line"></span><br><span class="line"><span class="comment">/*略.......*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 键盘初始化 */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">keyboard_init</span><span class="params">()</span> {</span><br><span class="line">   put_str(<span class="string">"keyboard init start\n"</span>);</span><br><span class="line">   ioqueue_init(&amp;kbd_buf);      <span class="comment">//初始化环形缓冲区</span></span><br><span class="line">   register_handler(<span class="number">0x21</span>, intr_keyboard_handler);</span><br><span class="line">   put_str(<span class="string">"keyboard init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这样重新编译写入运行之后，实现的效果就是如果一直输入，会发现输入63个字符之后，就无法继续输入了(屏幕上的显示就是键盘程序中<code>put_char(cur_char);</code>的体现)，符合代码里面的缓冲区写满的现象</p><hr><h3 id="H-5-测试生产者和消费实例"><a href="#H-5-测试生产者和消费实例" class="headerlink" title="H.5 测试生产者和消费实例"></a>H.5 测试生产者和消费实例</h3><p>这里为了测试，我们使用另外两个任务来作为消费者来读取缓冲区的内容,所以这里我们还要<strong>重新打开时钟的中断</strong>，同时在keyboard中添加对<strong>缓冲区的外部声明</strong>，使得外部的函数可以对其进行访问，最后在main函数中<strong>添加消费者的线程</strong>,记得要删除之前测试添加的代码</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"><span class="comment">/* 初始化可编程中断控制器 8259A */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">pic_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/*初始化主片 */</span> </span><br><span class="line">    outb (PIC_M_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x20</span>); <span class="comment">// ICW2: 起始中断向量号为 0x20 </span></span><br><span class="line">    <span class="comment">//也就是 IR[0-7] 为 0x20 ~ 0x27 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x04</span>); <span class="comment">// ICW3: IR2 接从片</span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line">    <span class="comment">/*初始化从片 */</span> </span><br><span class="line">    outb (PIC_S_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x28</span>); <span class="comment">// ICW2: 起始中断向量号为 0x28 </span></span><br><span class="line">    <span class="comment">// 也就是 IR[8-15]为 0x28 ~ 0x2F </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x02</span>); <span class="comment">// ICW3: 设置从片连接到主片的 IR2 引脚</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0xfc</span>);        <span class="comment">//打开时钟和键盘中断     </span></span><br><span class="line">    outb(PIC_S_DATA, <span class="number">0xff</span>);         <span class="comment">//其余全部关闭</span></span><br><span class="line">    put_str(<span class="string">" pic_init done\n"</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/keyboard.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __DEVICE_KEYBOARD_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __DEVICE_KEYBOARD_H</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="class"><span class="keyword">struct</span> <span class="title">ioqueue</span> <span class="title">kbd_buf</span>;</span>  <span class="comment">//声明缓冲区变量，使得外部可以访问</span></span><br><span class="line"><span class="comment">//键盘初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">keyboard_init</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>这里要删除这个临时的输出，显示效果会更好</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (cur_char) </span><br><span class="line">{</span><br><span class="line">      <span class="comment">//如果cur_char 不为0，则添加到ked_buf中(提前判断缓冲区是否满)</span></span><br><span class="line">      <span class="keyword">if</span>(!ioq_full(&amp;kbd_buf))</span><br><span class="line">      {</span><br><span class="line">          <span class="comment">// put_char(cur_char);     //临时的</span></span><br><span class="line">          ioq_putchar(&amp;kbd_buf,cur_char);</span><br><span class="line">      }</span><br><span class="line">   <span class="keyword">return</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"debug.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"memory.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"console.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ioqueue.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"keyboard.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                     <span class="comment">//初始化所有</span></span><br><span class="line">    <span class="comment">//创建一个线程，线程名，优先级，线程函数名，参数</span></span><br><span class="line">    thread_start(<span class="string">"k_thread_one"</span>,<span class="number">31</span>,k_thread_one,<span class="string">"A__:"</span>);   </span><br><span class="line">    thread_start(<span class="string">"k_thread_two"</span>,<span class="number">8</span>,k_thread_two,<span class="string">"B__:"</span>);</span><br><span class="line"></span><br><span class="line">    intr_enable();      <span class="comment">//打开中断，使时钟中断起作用</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        ;</span><br><span class="line">    }   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_one</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(!ioq_empty(&amp;kbd_buf))</span><br><span class="line">        {</span><br><span class="line">            console_put_str(para);  <span class="comment">//输出</span></span><br><span class="line">            <span class="type">char</span> byte = ioq_getchar(&amp;kbd_buf);      <span class="comment">//缓冲区获取</span></span><br><span class="line">            console_put_char(byte);</span><br><span class="line">        }</span><br><span class="line">        intr_set_status(old_status);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">k_thread_two</span><span class="params">(<span class="type">void</span>* arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/* 用 void*来通用表示参数，被调用的函数知道自己需要什么类型的参数，自己转换再用 */</span></span><br><span class="line">    <span class="type">char</span> * para = arg;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="class"><span class="keyword">enum</span> <span class="title">intr_status</span> <span class="title">old_status</span> =</span> intr_disable();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(!ioq_empty(&amp;kbd_buf))</span><br><span class="line">        {</span><br><span class="line">            console_put_str(para);  <span class="comment">//输出</span></span><br><span class="line">            <span class="type">char</span> byte = ioq_getchar(&amp;kbd_buf);      <span class="comment">//缓冲区获取</span></span><br><span class="line">            console_put_char(byte);</span><br><span class="line">        }</span><br><span class="line">        intr_set_status(old_status);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>编译写入运行之后的效果是，如果我长按一个键，那么屏幕上会显示出<code>A__:</code> 和 <code>B__:</code> 和按下的字符交替 </p><p>这一章就到此结束了</p><h2 id="I-用户进程前的小结"><a href="#I-用户进程前的小结" class="headerlink" title="I 用户进程前的小结"></a>I 用户进程前的小结</h2><p>本篇主要学了内存管理，内核进程，实现了信号量和锁，同时编写了键盘的输入输出驱动，整体代码量增多了，但是主要是一些数据结构，比如说队列，位图，用来实现唤醒缓冲区，就绪队列等等</p><p>下面就是来实现用户进程，实现安全隔离……</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li>[操作系统真象还原 (郑纲) (Z-Library)]   — 大家可以自己在网上查找相关资源</li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#49;&#x34;&#54;&#55;&#48;&#50;&#51;&#x36;&#50;&#x40;&#113;&#x71;&#46;&#99;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">这一章主要对《操作系统真相还原》的内存管理/线程/同步相关，本篇文章主要用来记录学习过程中遇到的问题，以及相关知识概念，方便后来查询......</summary>
    
    
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/categories/OS/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/OS/Linux/"/>
    
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/tags/OS/"/>
    
    <category term="《真相还原》" scheme="https://blog.haozi-haozi.cn/tags/%E3%80%8A%E7%9C%9F%E7%9B%B8%E8%BF%98%E5%8E%9F%E3%80%8B/"/>
    
  </entry>
  
  <entry>
    <title>真象还原 --内核/中断 study(2)</title>
    <link href="https://blog.haozi-haozi.cn/2025/10/24/os_elephant_two/"/>
    <id>https://blog.haozi-haozi.cn/2025/10/24/os_elephant_two/</id>
    <published>2025-10-24T06:13:00.000Z</published>
    <updated>2026-03-24T08:56:37.026Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A 前情提要"></a>A 前情提要</h2><p>在此之前，第一部分我们完成了基础的<strong>环境配置，bochs配置</strong>，以及<strong>MBR,loader</strong>的的基础编写，成功的进入了<strong>保护模式</strong>并且开启了<strong>内存分页功能</strong></p><blockquote><ul><li><a href="https://blog.haozi-haozi.cn/2025/10/21/os_elephant_one/">真象还原 –环境/准备 study(1)</a></li></ul></blockquote><p>这一部分主要开启对<strong>内核，中断</strong>进军</p><p><strong>ps:如果参考本系列文章来实操，需要结合《操作系统真象还原》一起观看，否则会缺失很多细节</strong></p><hr><h2 id="B-加载内核"><a href="#B-加载内核" class="headerlink" title="B. 加载内核"></a>B. 加载内核</h2><p>在之前的学习中，我们基本都是通过汇编来与及其对话，想想以前的驱动开发都是直接用C语言等语言来编写，然后操作系统来帮我们撑腰，但现在我们要用 C 语言写脱离操作系统的程序，必须要自己<strong>准备程序的入口地址</strong>，同时也<strong>不能用C标准库</strong>，因为系统调用的中断处理程序我们还没有准备</p><h3 id="B-1-用C语言写内核"><a href="#B-1-用C语言写内核" class="headerlink" title="B.1 用C语言写内核"></a>B.1 用C语言写内核</h3><p>这里我们现在之前的目录基础上创建一个目录<code>kernel</code>（以后与内核相关的文件都放在这个目录下面）,然后来实现我们自己的第一个程序来体验编译过程</p><p>这里首先介绍<code>gcc</code>编译器的基础使用:</p><blockquote><p><strong>gcc -c -o kernel/main.o kernel/main.c</strong><br>-c表示编译,汇编到目标代码(不链接)<br>-o表是将输出的文件以指定的文件名来存储，有同名文件存在的时候就覆盖掉</p></blockquote><p>首先我们创建第一个内核的文件:<code>main.c</code></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c  *****/</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在这个文件路径下执行指令</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get install gcc <span class="comment"># (如果没安装的话)</span></span><br><span class="line"></span><br><span class="line">gcc -c -o main.o main.c</span><br></pre></td></tr></table></figure></div><p>然后就可以看到main.o文件，但是目前只是一个目标文件(待重定位文件)<br><code>重定位指的是文件里面所用的符号还没有安排地址，这些符号的地址 需要将来与其他目标文件“组成”一个可执行文件时再重新定位编排地址</code></p><p>在 Linux 下用于链接的程序是 <strong>ld</strong>，链接有一个好处，<strong>可以指定最终生成的可执行文件的起始虚拟地址</strong>。它是<strong>用-Ttext 参数来指定</strong>的，所以咱们可以执行以下命令完成链接</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ld main.o -Ttext 0xc0001500 -e main -o kernel.bin</span><br></pre></td></tr></table></figure></div><p><code>-Ttext</code>用来地址初始虚拟地址是<code>0xc0001500</code>,这个地址是设计好的，在后面的加载内核中会具体了解<br><code>-e</code>用来指定程序的起始地址(这里不仅仅可以以数字形式的地址，也可以是符号名)，比如这里是<code>main</code>,用来知道程序从哪里开始执行（当然，如果主函数改成<code>void __start(void)</code> 那么就可以一气呵成不要这个指令了）<br><code>-o</code>用来指定输出的文件位置和名称</p><p>然后我们的目录下就出现了<code>kernel.bin</code>文件了</p><p>使用过gcc的人可能会问:直接使用gcc一口气编译链接生成的文件和我们分两步产生的bin文件有什么区别？</p><p>首先如果直接编译链接，生成的文件可能有4000字节+，但是手动的大概有1700字节+，因为编译器在编译过程中为咱们引用了别的代码，这就是 c 运行库的功劳，目的是在调用 main 函数前做初始化环境等工作(比如说__start入口符号等)</p><p>ok呀，这里也是大概举个main函数的例子来引入</p><hr><h3 id="B-2-elf文件简介"><a href="#B-2-elf文件简介" class="headerlink" title="B.2 elf文件简介"></a>B.2 elf文件简介</h3><p>之前我们是BIOS 调用mbr(0x7c00)，mbr调用loader(0x900),并且这些地址都是固定的，不灵活，调用方要提前与被调用方约定，那肯定会有更灵活的方法来让加载地址不那么固定吧</p><p>兄弟，有的有的，最简单的办法就是在程序文件中专门腾出个空间来写入这些程序的入口地址（主调程序再将信息读出来，然后加载到相应的入口地址，最后跳转即可），当然这里还可以添加分配内存什么的，这也就是<strong>文件头</strong>的由来</p><p>在原先的纯二进制文件中添加新的文件头，就形成了一个新的文件格式，其中，程序头可以自定义，只要我们按照自己定义的格式去解析就行</p><hr><ul><li>在Windows下，一个可执行文件的格式是PE(注意，EXE只是拓展名，是文件名的一部分，并不是格式)</li><li>在Linux下，一个可执行文件的格式是ELF,是指通过编译链接的二进制可执行文件，该文件可以直接运行</li></ul><p>为了避免混淆，和书作者一样采用与 ELF 规范相同的命名方式，本节中所说的目标文件即指各种类型符合 ELF规范的文件</p><p>至于如何弄清文件格式的本质，大家可以具体看看书中的解释，这里同样不过过多说明(因为也不是几句话能说明白的)</p><blockquote><p>ELF 文件格式依然分为文件头和文件体两部分,其中文件头很复杂，具体内容可以参考linux系统下的<code>/usr/include/elf.h</code>定义(最权威的)</p></blockquote><p>这里还是通过书中的实例来学习,这里原作者提供了一个sh脚本来方便使用(利用xxd指令逐字节输出文件的内容)，主要是避免每次复杂的键入，方便使用，这里也直接列出(添加了中文注释，方便理解)，我们来分析之前写的”内核”，也就是<code>kernel.bin</code>文件</p><div class="code-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#/home/mouse/OS_mouse/tool/bochs/mouse/xxd.sh</span></span><br><span class="line"><span class="comment">#usage: sh xxd.sh 文件 起始地址 长度</span></span><br><span class="line">xxd -u -a -g 1 -s <span class="variable">$2</span> -l <span class="variable">$3</span> <span class="variable">$1</span> </span><br><span class="line"></span><br><span class="line"><span class="comment"># 参数解释：</span></span><br><span class="line"><span class="comment"># -u 使用大写十六进制字母（默认小写）</span></span><br><span class="line"><span class="comment"># -a 自动跳过：用单个'*'替换空行（默认关闭）</span></span><br><span class="line"><span class="comment"># -g 1 每1个字节用空格分隔（默认正常模式为2字节）</span></span><br><span class="line"><span class="comment"># -s 从指定偏移量开始（参数$2）</span></span><br><span class="line"><span class="comment"># -l 读取指定长度（参数$3）</span></span><br><span class="line"><span class="comment"># $1 输入文件名</span></span><br></pre></td></tr></table></figure></div><p>然后我们就可以来分析文件了</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh xxd.sh kernel/kernel.bin 0 300  <span class="comment">#逐字节查看文件0~300地址的内容,注意执行的位置是在脚本所在路径</span></span><br></pre></td></tr></table></figure></div><p>结果如下：</p><blockquote><p>00000000: <strong>7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00</strong>  .ELF…………<br>00000010: <strong>02 00 3E 00 01 00 00 00 00 15 00 C0 00 00 00 00</strong>  ..&gt;………….<br>00000020: <strong>40 00 00 00 00 00 00 00 B0 16 00 00 00 00 00 00</strong>  @……………<br>00000030: <strong>00 00 00 00 40 00 38 00 02 00 40 00 07 00 04 00</strong>  ....@.8...@…..<br>00000040: <code>01 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00</code>  …………….<br>00000050: <code>00 00 00 C0 00 00 00 00 00 00 00 C0 00 00 00 00</code>  …………….<br>00000060: <code>40 15 00 00 00 00 00 00 40 15 00 00 00 00 00 00</code>  @.......@…….<br>00000070: <code>00 00 20 00 00 00 00 00</code> 51 E5 74 64 06 00 00 00  .. …..Q.td….<br>00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  …………….<br>00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  …………….<br>000000a0: 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  …………….<br>000000b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  …………….<br>*<br>00000120: 00 00 00 00 00 00 00 00 00 00 00 00              …………</p></blockquote><p>其中，加粗的部分是文件头(rlf header部分)，第二部分特殊标记的部分是程序头表(program header table),这和书中是不一样的，因为书中的系统是32位系统，文件头通常是52字节，而我的是64位系统，所以通常是64字节，当然是多少位在文件头中也有说明(比如0x04: 02,表示是64位ELF)</p><p>下面主要通过表格总结这两部分的具体内容:</p><h4 id="ELF-Header-结构（64-bit）"><a href="#ELF-Header-结构（64-bit）" class="headerlink" title="ELF Header 结构（64-bit）"></a>ELF Header 结构（64-bit）</h4><table><thead><tr><th>偏移</th><th>字段</th><th>说明</th></tr></thead><tbody><tr><td>0x00-0x03</td><td>7F 45 4C 46</td><td>ELF 魔数（<code>\x7FELF</code>）</td></tr><tr><td>0x04</td><td>02</td><td>64 位 ELF（<code>01</code> 是 32 位）</td></tr><tr><td>0x05</td><td>01</td><td>小端序（<code>02</code> 是大端序）</td></tr><tr><td>0x10-0x11</td><td>02 00</td><td>可执行文件（<code>ET_EXEC</code>）</td></tr><tr><td>0x12-0x13</td><td>3E 00</td><td>x86-64 架构（<code>0x3E</code>）</td></tr><tr><td>0x18-0x1F</td><td>00 15 00 C0…</td><td>程序入口地址（<code>0xC0001500</code>）</td></tr><tr><td>0x20-0x27</td><td>40 00 00 00…</td><td>Program Header Table 偏移（<code>0x40</code>）</td></tr><tr><td>0x28-0x2F</td><td>B0 16 00 00…</td><td>Section Header Table 偏移（<code>0x16B0</code>）</td></tr><tr><td>0x36-0x37</td><td>38 00</td><td>Program Header 大小（56 字节）</td></tr><tr><td>0x38-0x39</td><td>07 00</td><td>Program Header 数量（7 个）</td></tr></tbody></table><h4 id="Program-Header-条目结构（64-bit）"><a href="#Program-Header-条目结构（64-bit）" class="headerlink" title="Program Header 条目结构（64-bit）"></a>Program Header 条目结构（64-bit）</h4><table><thead><tr><th>偏移</th><th>字段</th><th>说明</th></tr></thead><tbody><tr><td>0x40-0x43</td><td>01 00 00 00</td><td><code>PT_LOAD</code>（可加载段）</td></tr><tr><td>0x44-0x47</td><td>05 00 00 00</td><td>Flags（<code>R+X</code>，可读可执行）</td></tr><tr><td>0x48-0x4F</td><td>00 00 00 00…</td><td>文件偏移（<code>0x0</code>）</td></tr><tr><td>0x50-0x57</td><td>00 00 00 C0…</td><td>虚拟地址（<code>0xC0000000</code>）</td></tr><tr><td>0x58-0x5F</td><td>00 00 00 C0…</td><td>物理地址（通常同虚拟地址）</td></tr><tr><td>0x60-0x67</td><td>40 15 00 00…</td><td>段大小（<code>0x1540</code> 字节）</td></tr><tr><td>0x68-0x6F</td><td>40 15 00 00…</td><td>内存大小（<code>0x1540</code> 字节）</td></tr><tr><td>0x70-0x77</td><td>00 00 20 00…</td><td>对齐（<code>0x200000</code>）</td></tr></tbody></table><hr><h3 id="B-3-将内核载入内存"><a href="#B-3-将内核载入内存" class="headerlink" title="B.3 将内核载入内存"></a>B.3 将内核载入内存</h3><p>已经期待了很久将内核载入内存</p><p>我们的内核文件是 kernel.bin，这个文件是由 loader 将其从硬盘上读出并加载到内存中的，到此，接力棒传到了最后一个选手的手里，也就是说我们现在需要将kernel.bin文件定入硬盘</p><p>先回忆一下布局： 0扇区存放着MBR 1扇区空着的 2扇区写入的loader文件(1.3kb),而我们的一个扇区是512字节，所以这里差不多会占用三个扇区，所以2~4扇区也不能使用了，所以这里我们原则要写入5扇区了，但是为了loader可能会有拓展，以及分开更安心，所以这里<strong>选择第9个扇区</strong></p><p>开始操作:</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 因为内核文件的大小会一直改变，但是每次修改count也会很麻烦，所以这里直接修改成200(因为将来的内核大小不会超过100kb)，同时dd指令也会自己判断写入的数据量</span></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc</span><br></pre></td></tr></table></figure></div><p>加上之前的编译，链接，可能指令会很长，所以将这个指令联合之前的指令都写进一个脚本，方便后面执行使用(同时，如果后面有文件名修改，也同步修改脚本即可)，当然，这个脚本不是那么完善，比如不支持多文件，或错误提示等，后面会再添加更新的</p><div class="code-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel_start.sh</span></span><br><span class="line"><span class="comment">#编译 链接 写入</span></span><br><span class="line">gcc -c -o main.o main.c &amp;&amp; ldmain.o -Ttext 0xc0001500 -e main -o kernel.bin &amp;&amp; <span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc</span><br></pre></td></tr></table></figure></div><p>这样我们在kernel文件夹下执行下面的执行便可以一气呵成完成编译链接写入了</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh kernel_start.sh</span><br></pre></td></tr></table></figure></div><hr><ul><li><strong>最后这里还要修改loader.S文件</strong></li></ul><p>主要修改两个地方，</p><ol><li><strong>加载内核</strong> ：将内核加载到内存缓冲区，这里选择在分页前开始加载</li><li><strong>初始化内核</strong>：在分页后，将加载寄哪里的elf内核文件放到对应的虚拟内存地址，然后跳过去执行，至此loader的工作到此结束</li></ol><p>首先进行第一步，加载内核，这里就涉及到我应该加载到什么地方？</p><p>首先我们的内核很小，在低端1MB中安身即可，这里首先要考虑之前的的其它重要数据来分配，我们最终选择<code>0x70000～0x9fbff</code>的堵住开安身(问就是书作者决定的)，差不多有190kb，完全够我们的内核使用</p><blockquote><p>内核被加载到内存后，loader 还要通过分析其 elf 结构将其展开到新的位置，所以说，内核在内存中有两份拷贝，一份是 elf 格式的原文件 kernel.bin，另一份是 loader 解析 elf 格式的 kernel.bin 后在内存中生成的内核映像（也就是将程序中的各种段 segment 复制到内存后的程序体），这个映像才是真正运行的内核</p></blockquote><p>其实既然决定好位置了，我们先在<code>boot.inc</code>文件中添加新的符号</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">;/home/mouse/OS_mouse/tool/bochs/mouse/boot.inc</span><br><span class="line">;--------------------- loader &amp; kernel -------------------------</span><br><span class="line">KERNEL_START_SECTOR equ <span class="number">0x9</span>             ;kernel.bin 所在的扇区号 <span class="number">9</span></span><br><span class="line">KERNEL_BIN_BASE_ADDR    equ <span class="number">0x70000</span>     ;从磁盘读出后写入的内存地址</span><br><span class="line">KERNEL_ENTRY_POINT  equ <span class="number">0xc0001500</span>      ;虚拟地址入口</span><br><span class="line"></span><br><span class="line">;--------------------- elf -------------------------------------</span><br><span class="line">PT_NULL equ <span class="number">0</span>   ; ELF 程序头类型：未使用</span><br><span class="line">;略......</span><br></pre></td></tr></table></figure></div><p>下面给出loader中添加的加载内核的代码,因为之前的代码其实已经有点长了，如果全部直接贴到这里显得浪费长度，也不好理解，所以后面的所有代码都是截取对应的函数来列举，当然，<strong>在文章末尾会列出本篇文章中用的所有文件的完整版</strong></p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"><span class="comment">; 略......</span></span><br><span class="line"><span class="comment">;------------------------------- 加载kernel------------------------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,KERNEL_START_SECTOR     <span class="comment">;kernel.bin所在的扇区号(9)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,KERNEL_BIN_BASE_ADDR    <span class="comment">;记录写入的地址</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">200</span>                     <span class="comment">;读入的扇区数</span></span><br><span class="line">    <span class="keyword">call</span> rd_disk_m_32               <span class="comment">;eax、ebx、ecx 是函数 rd_disk_m_32 的三个参数，用于从硬盘上读取文件,与MBR中读取差不多</span></span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="keyword">call</span> setup_page     <span class="comment">; 创建页目录及页表并初始化页内存位图</span></span><br><span class="line"><span class="comment">; 略......</span></span><br></pre></td></tr></table></figure></div><p>下面是初始化内核的代码:<br><strong>需要注意的是因为我是64系统下的，所以编译的结果elf也是64位，而我们的操作系统需要的是32位，所以这里修改修改我们之前的编译过程，即添加<code>-m32</code>参数</strong></p><p>这里重新给出脚本的内容，<strong>注意在mbr.S文件里面有一行<code>mov cx,4</code>，是用来选择读入扇区的大小的，这里要修改成7,防止loader文件后续继续变大被截短了</strong></p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># /home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel_start<span class="number">.</span>sh</span><br><span class="line"></span><br><span class="line">BASE_DIR=<span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse/kernel"</span></span><br><span class="line"></span><br><span class="line">cd $BASE_DIR</span><br><span class="line"></span><br><span class="line">#编译 链接 写入<span class="number">32</span>位版本 </span><br><span class="line">gcc -m32 -c -o main<span class="number">.</span>o main<span class="number">.</span>c &amp;&amp; ld -m elf_i386 main<span class="number">.</span>o -Ttext <span class="number">0xc0001500</span> -e main -o kernel<span class="number">.</span>bin &amp;&amp; <span class="built_in">dd</span> if=$BASE_DIR/kernel<span class="number">.</span>bin of=/home/mouse/OS_mouse/tool/bochs/hd60M<span class="number">.</span>img bs=<span class="number">512</span> count=<span class="number">200</span> seek=<span class="number">9</span> conv=notrunc</span><br></pre></td></tr></table></figure></div><p>同理给出loader文件的脚本吧，否则每次输入很多指令也挺烦的,后期会创建一个boot文件夹(位于/mouse/boot),然后将include文件也移动到这个位置，目前还是直接在mouse文件夹下</p><div class="code-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /home/mouse/OS_mouse/tool/bochs/mouse/loader_mbr_start.sh</span></span><br><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置基础路径变量</span></span><br><span class="line">BASE_DIR=<span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse"</span></span><br><span class="line">HD_IMG=<span class="string">"/home/mouse/OS_mouse/tool/bochs/hd60M.img"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 进入工作目录</span></span><br><span class="line"><span class="built_in">cd</span> <span class="variable">$BASE_DIR</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译</span></span><br><span class="line">nasm -I include/ -o mbr.bin mbr.S</span><br><span class="line">nasm -I include/ -o loader.bin loader.S</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入，注意这里是7</span></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=<span class="variable">$BASE_DIR</span>/mbr.bin of=<span class="variable">$HD_IMG</span> bs=512 count=1 seek=0 conv=notrunc</span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=<span class="variable">$BASE_DIR</span>/loader.bin of=<span class="variable">$HD_IMG</span> bs=512 count=7 seek=2 conv=notrunc</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>同时之前给出的表格elf的内容也是32位的，会与下面写内核的具体内容不同，所以这里也更新一遍表格如下(例子为新编译的文件)</p><hr><h4 id="ELF-Header-结构（32-bit）"><a href="#ELF-Header-结构（32-bit）" class="headerlink" title="ELF Header 结构（32-bit）"></a>ELF Header 结构（32-bit）</h4><table><thead><tr><th>偏移</th><th>字段</th><th>说明</th></tr></thead><tbody><tr><td>0x00-0x03</td><td><code>7F 45 4C 46</code></td><td>ELF 魔数（<code>\x7FELF</code>）</td></tr><tr><td>0x04</td><td><code>01</code></td><td>32 位 ELF（<code>02</code> 是 64 位）</td></tr><tr><td>0x05</td><td><code>01</code></td><td>小端序（<code>02</code> 是大端序）</td></tr><tr><td>0x10-0x11</td><td><code>02 00</code></td><td>可执行文件（<code>ET_EXEC</code>）</td></tr><tr><td>0x12-0x13</td><td><code>03 00</code></td><td>x86 架构（<code>0x03</code>）</td></tr><tr><td>0x18-0x1B</td><td><code>00 15 00 C0</code></td><td>程序入口地址（<code>0xC0001500</code>）</td></tr><tr><td>0x1C-0x1F</td><td><code>34 00 00 00</code></td><td>Program Header Table 偏移（<code>0x34</code>）</td></tr><tr><td>0x20-0x23</td><td><code>60 06 00 00</code></td><td>Section Header Table 偏移（<code>0x660</code>）</td></tr><tr><td>0x2A-0x2B</td><td><code>20 00</code></td><td>Program Header 大小（32 字节）</td></tr><tr><td>0x2C-0x2D</td><td><code>07 00</code></td><td>Program Header 数量（7 个）</td></tr></tbody></table><hr><h4 id="Program-Header-条目结构（32-bit）"><a href="#Program-Header-条目结构（32-bit）" class="headerlink" title="Program Header 条目结构（32-bit）"></a>Program Header 条目结构（32-bit）</h4><table><thead><tr><th>偏移</th><th>字段</th><th>说明</th></tr></thead><tbody><tr><td>0x34-0x37</td><td><code>01 00 00 00</code></td><td><code>PT_LOAD</code>（可加载段）</td></tr><tr><td>0x38-0x3B</td><td><code>00 10 00 C0</code></td><td>虚拟地址（<code>0xC0001000</code>）</td></tr><tr><td>0x3C-0x3F</td><td><code>00 10 00 C0</code></td><td>物理地址（通常同虚拟地址）</td></tr><tr><td>0x40-0x43</td><td><code>3C 05 00 00</code></td><td>段大小（<code>0x53C</code> 字节）</td></tr><tr><td>0x44-0x47</td><td><code>3C 05 00 00</code></td><td>内存大小（<code>0x53C</code> 字节）</td></tr><tr><td>0x48-0x4B</td><td><code>05 00 00 00</code></td><td>Flags（<code>R+X</code>，可读可执行）</td></tr><tr><td>0x4C-0x4F</td><td><code>00 10 00 00</code></td><td>对齐（<code>0x1000</code>）</td></tr></tbody></table><hr><p>回归正题，下面是初始化内核的代码:</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;----------------------将kernel.bin中的 segment(段) 拷贝到编译的地址-----------------------</span></span><br><span class="line"><span class="symbol">kernel_init:</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">eax</span>, <span class="built_in">eax</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ebx</span>, <span class="built_in">ebx</span> <span class="comment">;ebx 记录程序头表地址</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ecx</span>, <span class="built_in">ecx</span> <span class="comment">;cx 记录程序头表中的 program header 数量</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">edx</span>, <span class="built_in">edx</span> <span class="comment">;dx 记录 program header 尺寸，即 e_phentsize</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,[KERNEL_BIN_BASE_ADDR +<span class="number">42</span>]   <span class="comment">;偏移42位的属性是e_phentsize，记录program header 大小</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,[KERNEL_BIN_BASE_ADDR +<span class="number">28</span>]  <span class="comment">;偏移28位记录的是e_phoff，表示第一个program header的偏移量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,KERNEL_BIN_BASE_ADDR</span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,[KERNEL_BIN_BASE_ADDR +<span class="number">44</span>]   <span class="comment">;偏移44位记录e_phum,表示有几个program header</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.each_segment:</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">byte</span> [<span class="built_in">ebx</span> + <span class="number">0</span>],PT_NULL      <span class="comment">;若p_type == PT_NULL,则program header未使用</span></span><br><span class="line">    <span class="keyword">je</span> .PT_NULL                     <span class="comment">;则跳转到 .PT_NULL</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;函数 memcpy(dst,src,size)  为函数 memcpy 压入参数，参数是从右往左依然压入</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">dword</span> [<span class="built_in">ebx</span> + <span class="number">16</span>]           <span class="comment">;偏移16位记录p_filesz   是压入函数 memcpy 的第三个参数：size  </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, [<span class="built_in">ebx</span> + <span class="number">4</span>]              <span class="comment">;偏移4位记录p_offset   </span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, KERNEL_BIN_BASE_ADDR   <span class="comment">;加上 kernel.bin 被加载到的物理地址，eax 为该段的物理地址</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">eax</span>                        <span class="comment">;压入函数 memcpy 的第二个参数：源地址</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">dword</span> [<span class="built_in">ebx</span> + <span class="number">8</span>]            <span class="comment">;偏移8位记录p_vaddr     是压入函数 memcpy 的第一个参数：目的地址</span></span><br><span class="line">    <span class="keyword">call</span> mem_cpy                    <span class="comment">;调用 mem_cpy 完成段复制</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">12</span>                      <span class="comment">;清理栈中压入的三个参数，不清理会爆栈的哦</span></span><br><span class="line"><span class="symbol">.PTNULL:</span> </span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,<span class="built_in">edx</span>                     <span class="comment">;edx 为 program header 大小，即 e_phentsize,;在此 ebx 指向下一个 program header</span></span><br><span class="line">    <span class="keyword">loop</span> .each_segment</span><br><span class="line">    <span class="keyword">ret</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;---------- 逐字节拷贝函数 mem_cpy（dst，src，size） ------------ </span></span><br><span class="line"><span class="comment">;输入:栈中三个参数（dst，src，size）</span></span><br><span class="line"><span class="comment">;输出:无</span></span><br><span class="line"><span class="comment">;---------------------------------------------------------------</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">mem_cpy:</span></span><br><span class="line">    <span class="keyword">cld</span>             <span class="comment">;清除方向标志（DF=0），确保字符串操作正向移动（esi/edi递增</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ebp</span>        <span class="comment">;保存调用者的栈基址 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebp</span>,<span class="built_in">esp</span>     <span class="comment">;建立新的栈帧（ebp指向当前栈顶）</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>        <span class="comment">;rep 指令用到了 ecx 但 ecx 对于外层段的循环还有用，所以先入栈备份</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edi</span>, [<span class="built_in">ebp</span> + <span class="number">8</span>]      <span class="comment">; 加载目标地址（dst） </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, [<span class="built_in">ebp</span> + <span class="number">12</span>]     <span class="comment">; 加载源地址（src）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, [<span class="built_in">ebp</span> + <span class="number">16</span>]     <span class="comment">; 加载要复制的字节数（size）</span></span><br><span class="line">    <span class="keyword">rep</span> <span class="keyword">movsb</span>               <span class="comment">; 逐字节拷贝,执行ecx次</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ecx</span>                 <span class="comment">; 恢复ecx</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ebp</span>                 <span class="comment">; 恢复调用者的栈基址</span></span><br><span class="line">    <span class="keyword">ret</span>                     <span class="comment">; 返回</span></span><br></pre></td></tr></table></figure></div><blockquote><p>函数 kernel_init 的作用是将 kernel.bin 中的段（segment）拷贝到各段自己被编译的虚拟地址处,将这些段单独提取到内存中，这就是平时所说的内存中的程序映像，其它的部分可以具体参考书中内容</p></blockquote><hr><p>最后还有一点点添加的地方,也就是调用</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"><span class="comment">; 略.....</span></span><br><span class="line">    <span class="comment">;在开启分页后，用 gdt 新的地址重新加载</span></span><br><span class="line">    <span class="keyword">lgdt</span> [gdt_ptr]                          <span class="comment">;重新加载</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> <span class="built_in">dword</span> SELECTOR_CODE:enter_kernel    <span class="comment">; 刷新流水线,虽然已经是32位了，不用刷新，但还是添加上，防止出现莫名的问题</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">enter_kernel:</span></span><br><span class="line">    <span class="comment">;初始化gs寄存器，显存相关</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_VIDEO      <span class="comment">;这里要初始化gs寄存器，其实我尝试将它放在之前的位置初始化也可以打印成功好像</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">gs</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">160</span>], <span class="string">'M'</span>      <span class="comment">;视频段段基址已经被更新，用字符 M 表示 mouse 其实也是验证真的执行没</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">call</span> kernel_init            <span class="comment">;初始化内核</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esp</span>,<span class="number">0xc009f000</span>          <span class="comment">;新的栈起始地址0xc0000900</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> KERNEL_ENTRY_POINT      <span class="comment">;用地址 0x1500 访问测试，指为0xc0001500，也就是ld链接的虚拟地址的main函数</span></span><br><span class="line"><span class="comment">; 略.....</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p><code>boot.inc</code>的代码仅仅添加了4行这里就不打出来了,<code>main.c</code>也只是一个示例，这里也就不列出来了</p><ul><li><strong>下面是loader.S的具体代码</strong>，有需要可以展开看</li></ul><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>loader.S： 点击查看更多 </summary>              <div class="content">              <div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> loader vstart=LOADER_BASE_ADDR</span><br><span class="line">LOADER_STACK_TOP <span class="built_in">equ</span> LOADER_BASE_ADDR</span><br><span class="line"><span class="keyword">jmp</span> loader_start</span><br><span class="line"></span><br><span class="line"><span class="comment">; 构建gdt及其内部的描述符,拆分为上4字节，下4字节</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">GDT_BASE:</span>   <span class="built_in">dd</span> <span class="number">0x00000000</span>       <span class="comment">;空描述段</span></span><br><span class="line">            <span class="built_in">dd</span> <span class="number">0x00000000</span>       </span><br><span class="line"><span class="symbol">CODE_DESC:</span>  <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">            <span class="built_in">dd</span> DESC_CODE_HIGH4    </span><br><span class="line"><span class="symbol">DATA_STACK_DESC:</span>    <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">                    <span class="built_in">dd</span> DESC_DATA_HIGH4 </span><br><span class="line"><span class="symbol">VIDEO_DESC:</span> <span class="built_in">dd</span> <span class="number">0x80000007</span>               <span class="comment">;limit=(0xbffff-0xb8000)/4k=0x7 </span></span><br><span class="line">            <span class="built_in">dd</span> DESC_VIDEO_HIGH4         <span class="comment">;此时 dpl 为 0 </span></span><br><span class="line">GDT_SIZE    <span class="built_in">equ</span> $ - GDT_BASE </span><br><span class="line">GDT_LIMIT   <span class="built_in">equ</span> GDT_SIZE - <span class="number">1</span> </span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">50</span> <span class="built_in">dq</span> <span class="number">0</span> <span class="comment">; 此处预留 50 个描述符的空位(为什么不是60？因为我开头调用jmp，使得total_mem_bytes无法刚好是0xb00)  times 是 nasm 提供的伪指令，用来重复执行 times 后面表达式(编译器执行)</span></span><br><span class="line"></span><br><span class="line">SELECTOR_CODE <span class="built_in">equ</span> (<span class="number">0x0001</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 </span><br><span class="line"> <span class="comment">; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 </span></span><br><span class="line">SELECTOR_DATA <span class="built_in">equ</span> (<span class="number">0x0002</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line">SELECTOR_VIDEO <span class="built_in">equ</span> (<span class="number">0x0003</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">0x200</span> - ($ -$$) <span class="built_in">db</span> <span class="number">0</span>  <span class="comment">;填充0，使得total_mem_bytes 的节内偏移一定为0x200,地址一定为0xb00</span></span><br><span class="line"></span><br><span class="line">total_mem_bytes <span class="built_in">dd</span> <span class="number">0</span></span><br><span class="line"><span class="comment">; total_mem_bytes 用于保存内存容量，以字节为单位，这个位置比价好记</span></span><br><span class="line"><span class="comment">; 当前偏移loader.bin文件头0x200 字节</span></span><br><span class="line"><span class="comment">; loader.bin加载地址为 0x900</span></span><br><span class="line"><span class="comment">; 所以 total_mem_bytes 内存地址为 0xb00</span></span><br><span class="line"><span class="comment">; 将来在内核中我们会引用这个地址，同时等会验证内存容量的时候，GDB调试也可以通过读取这个地址的内容来查看大小</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;以下是 gdt 的指针，前 2 字节是 gdt 界限，后 4 字节是 gdt 起始地址</span></span><br><span class="line"><span class="meta">align</span> <span class="number">4</span>     <span class="comment">;强制对齐，用来解GDT base = 0xc0000903的问题</span></span><br><span class="line"><span class="symbol">gdt_ptr:</span> </span><br><span class="line">        <span class="built_in">dw</span> GDT_LIMIT </span><br><span class="line">        <span class="built_in">dd</span> GDT_BASE </span><br><span class="line">        loadermsg <span class="built_in">db</span> <span class="string">'Mosue'</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;人工对齐计算大小:total_mem_bytes(4) + gdt_ptr(6) + ards_buf(239) + ards_nr(2) + loadermsg（5） = 256字节</span></span><br><span class="line">ards_buf <span class="built_in">times</span> <span class="number">244</span> <span class="built_in">db</span> <span class="number">0</span>     <span class="comment">;填充对齐使用</span></span><br><span class="line">ards_nr <span class="built_in">dw</span> <span class="number">0</span>                <span class="comment">;用来记录ARDS结构体数量</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">loader_start:</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------0xe820 获取内存方法----------------------</span></span><br><span class="line"><span class="comment">;int 15h eax = 0000E820h , edx = 534D4150h ("SMAP") 获取内存布局</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ebx</span>,<span class="built_in">ebx</span>             <span class="comment">;第一次调用，ebx的值设置为0</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="number">0x534D4150</span>      <span class="comment">;赋值一次，以后循环的时候不会变化</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">di</span>,ards_buf         <span class="comment">;ards结构体缓冲区</span></span><br><span class="line"><span class="symbol">.e820_mem_get_loop:</span>         <span class="comment">;循环或得每个ARDS结构体</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,<span class="number">0x0000e820</span>      <span class="comment">;执行int 0x15 之后，eax的值会变成0x534D4150,所以每次执行int之前都要更新为子功能号(即0x0000e820)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">20</span>              <span class="comment">;ARDS地址范围描述符的结构大小为20字节</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                <span class="comment">;执行中断</span></span><br><span class="line">    <span class="keyword">jc</span> .e820_failed_so_try_e801     <span class="comment">;如果cf位为1，则说明有错误发生，那么尝试使用0xe801功能</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">di</span>,<span class="built_in">cx</span>               <span class="comment">;使di增加字节指向缓冲区中新的ARDS结构体的位置</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">word</span> [ards_nr]      <span class="comment">;记录ARDS数量</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">ebx</span>,<span class="number">0</span>               <span class="comment">;若ebx为0，且cf不为1，表示ards全部返回，也就是内存读取完毕了</span></span><br><span class="line">    <span class="keyword">jnz</span> .e820_mem_get_loop</span><br><span class="line"></span><br><span class="line"><span class="comment">;在所有ards结构中，找出((base_add_low + length_low)的最大值，即内存的容量</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,[ards_nr]        <span class="comment">;遍历每一个ARDS结构体，循环次数为ARDS的数量（cx寄存器）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,ards_buf</span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">edx</span>,<span class="built_in">edx</span>             <span class="comment">;edx即为最大内存容量，在此先请0</span></span><br><span class="line"><span class="symbol">.find_max_mem_area:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,[<span class="built_in">ebx</span>]           <span class="comment">;base_add_low</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>,[<span class="built_in">ebx</span>+<span class="number">8</span>]         <span class="comment">;length_low</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,<span class="number">20</span>              <span class="comment">;指向下一个ARDS结构</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>             <span class="comment">;比较当前最大值(edx)和新计算的值(eax)</span></span><br><span class="line">    <span class="keyword">jge</span> .next_ards          <span class="comment">;冒泡思想找出最大，edx寄存器始终是最大的内存容量，如果edx&gt;=eax,则跳过更新</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>             <span class="comment">;否则更新edx,edx 为总内存大小</span></span><br><span class="line"><span class="symbol">.next_ards:</span></span><br><span class="line">    <span class="keyword">loop</span> .find_max_mem_area <span class="comment">;直到(cx--)清零</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">;----------------------int 15h ax = 0xe801h 获取内存方法 最大支持4G----------------------</span></span><br><span class="line"> <span class="comment">;返回后ax，cx只一样，kb为单位，bx，dx值一样，以64kb为单位</span></span><br><span class="line"> <span class="comment">;在ax和cx寄存器中的为低15MB，bx和dx中为16MB到4GB</span></span><br><span class="line"><span class="symbol"> .e820_failed_so_try_e801:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="number">0xe801</span>               <span class="comment">;选择0xe801方法</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                    <span class="comment">;调用中断</span></span><br><span class="line">    <span class="keyword">jc</span> .e801_failed_so_try_e88  <span class="comment">;如果调用失败，尝试使用0x88方法</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;1.先计算低15MB的内存数量，并将kb转换为byte为单位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">0x400</span>            <span class="comment">;cx与ax一样，cx用作乘数</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">cx</span>                  <span class="comment">;dx:ax = 1024*ax</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">edx</span>,<span class="number">16</span>              <span class="comment">;edx &lt;&lt; 16 </span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">eax</span>,<span class="number">0x0000FFFF</span>      <span class="comment">;eax &amp;= 0x0000FFFF</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>              <span class="comment">;edx = edx | eax 得到完整的地址</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">0x100000</span>        <span class="comment">;ax只是15MB，所以要添加1MB</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="built_in">edx</span>             <span class="comment">;先把低15MB的内存容量存取esi寄存器备份</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;2.将16MB以上的内存转换为byte为单位</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">eax</span>,<span class="built_in">eax</span>             <span class="comment">;将eax的值清零</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">bx</span>               </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">0x10000</span>         <span class="comment">;0x10000:64kb</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">ecx</span>                 <span class="comment">;32位乘法，默认被乘数为eax,积为64位</span></span><br><span class="line">                            <span class="comment">;高32为存入edx,低32位存入eax,但是这个方法只能检测4GB，所以32位eax就够(edx一定为0)，所以直接相加eax即可</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esi</span>,<span class="built_in">eax</span>             <span class="comment">;将15MB以下的结果和低32位的结果相加</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="built_in">esi</span>             <span class="comment">;edx即为总内存大小</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">;----------------------int 15h ah = 0x88 获取内存方法 最大支持64MB----------------------</span></span><br><span class="line"> <span class="comment">;返回后ax,以kb为单位的内存容量，这里同样转换为byte,注意0x88子功能只会返回1MB以上的内容，所以最后要添加1MB</span></span><br><span class="line"><span class="symbol">.e801_failed_so_try_e88:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="number">ah</span>,<span class="number">0x88</span>             <span class="comment">;0x88方法</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                <span class="comment">;进入中断</span></span><br><span class="line">    <span class="keyword">jc</span> .error_hlt           <span class="comment">;如果调用失败，进入error_hlt</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">eax</span>,<span class="number">0x0000FFFF</span>      <span class="comment">;取低16位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">0x400</span>            <span class="comment">;1024</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">cx</span>                  <span class="comment">;dx:ax = cx*ax</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">edx</span> ,<span class="number">16</span>             <span class="comment">;edx &lt;&lt; 16</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>              <span class="comment">;edx = edx | eax</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">0x100000</span>        <span class="comment">;添加1MB，edx就是最终内存了</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功   其实可以不用跳转，因为下一个指令就是了</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.mem_get_ok:</span> <span class="comment">;获取内存成功</span></span><br><span class="line">    <span class="keyword">mov</span> [total_mem_bytes],<span class="built_in">edx</span>   <span class="comment">;将内存大小(byte)写入total_mem_bytes保存</span></span><br><span class="line">    <span class="keyword">jmp</span> .print_int              <span class="comment">;打印个mouse</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.error_hlt:</span>                     <span class="comment">;失败情况下</span></span><br><span class="line">    <span class="keyword">jmp</span> $</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号:0x13 功能描述:打印字符串</span></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入: </span></span><br><span class="line"><span class="comment">;AH 子功能号=13H </span></span><br><span class="line"><span class="comment">;BH = 页码</span></span><br><span class="line"><span class="comment">;BL = 属性(若 AL=00H 或 01H) </span></span><br><span class="line"><span class="comment">;CX=字符串长度</span></span><br><span class="line"><span class="comment">;(DH､ DL)=坐标(行 列､ ) </span></span><br><span class="line"><span class="comment">;ES:BP=字符串地址</span></span><br><span class="line"><span class="comment">;AL=显示输出方式</span></span><br><span class="line"><span class="comment">; 0—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 1—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置改变</span></span><br><span class="line"><span class="comment">; 2—字符串中含显示字符和显示属性。显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 3—字符串中含显示字符和显示属性。显示后，光标位置改变</span></span><br><span class="line"><span class="comment">;无返回值</span></span><br><span class="line"><span class="symbol">.print_int:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">sp</span>, LOADER_BASE_ADDR </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bp</span>, loadermsg   <span class="comment">; ES:BP = 字符串地址</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">5</span>          <span class="comment">; CX = 字符串长度</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0x1301</span>      <span class="comment">; AH = 13, AL = 01h </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0x001f</span>      <span class="comment">; 页号为 0(BH = 0) 蓝底粉红字(BL = 1fh) </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1800</span> </span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x10</span>            <span class="comment">; 10h 号中断</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;-------------------- 准备进入保护模式 ------------------------------- </span></span><br><span class="line">    <span class="comment">;1 打开 A20 </span></span><br><span class="line">    <span class="comment">;2 加载 gdt </span></span><br><span class="line">    <span class="comment">;3 将 cr0 的 pe 位置 1 </span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 1. 打开 A20 ---------------- </span></span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">al</span>,<span class="number">0x92</span> </span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">al</span>,<span class="number">0000_0010B</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="number">0x92</span>,<span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 2. 加载 GDT ---------------- </span></span><br><span class="line">    <span class="keyword">lgdt</span> [gdt_ptr] </span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 3. cr0 第 0 位置 1 ---------------- </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="built_in">cr0</span> </span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, <span class="number">0x00000001</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cr0</span>, <span class="built_in">eax</span> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> <span class="built_in">dword</span> SELECTOR_CODE:p_mode_start <span class="comment">; 刷新流水线</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------- 保护模式 -----------------------------</span></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>] </span><br><span class="line"><span class="symbol">p_mode_start:</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_DATA </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ds</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">es</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ss</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esp</span>,LOADER_STACK_TOP </span><br><span class="line"></span><br><span class="line"><span class="comment">;------------------------------- 加载kernel------------------------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,KERNEL_START_SECTOR     <span class="comment">;kernel.bin所在的扇区号(9)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,KERNEL_BIN_BASE_ADDR    <span class="comment">;记录写入的地址</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">200</span>                     <span class="comment">;读入的扇区数</span></span><br><span class="line">    <span class="keyword">call</span> rd_disk_m_32               <span class="comment">;eax、ebx、ecx 是函数 rd_disk_m_32 的三个参数，用于从硬盘上读取文件,与MBR中读取差不多</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">call</span> setup_page     <span class="comment">; 创建页目录及页表并初始化页内存位图</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;要将描述符表地址及偏移量写入内存 gdt_ptr，一会儿用新地址重新加载</span></span><br><span class="line">    <span class="keyword">sgdt</span> [gdt_ptr]      <span class="comment">; 存储到原来gdt所有的位置</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;将gdt描述符中视频段描述符中的段基址+0xc0000000</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,[gdt_ptr +<span class="number">2</span>]</span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">dword</span> [<span class="built_in">ebx</span> + <span class="number">0x18</span> +<span class="number">4</span>],<span class="number">0xc0000000</span></span><br><span class="line">    <span class="comment">;视频段时第3个段描述符，每个描述符是8字节，即0x18</span></span><br><span class="line">    <span class="comment">;段描述符的高 4 字节的最高位是段基址的第 31～24 位</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;将 gdt 的基址加上 0xc0000000 使其成为内核所在的高地址</span></span><br><span class="line">    <span class="comment">;add dword [gdt_ptr + 2],0xc0000000</span></span><br><span class="line">    <span class="comment">;mov ebx, [gdt_ptr + 2]</span></span><br><span class="line">    <span class="comment">;add ebx, 0xc0000000</span></span><br><span class="line">    <span class="comment">;mov [gdt_ptr + 2], ebx</span></span><br><span class="line">    <span class="keyword">movzx</span> <span class="built_in">eax</span>, <span class="built_in">word</span> [gdt_ptr + <span class="number">0</span>]    <span class="comment">; 取 limit (但其实只是顺序安全)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, <span class="built_in">dword</span> [gdt_ptr + <span class="number">2</span>]     <span class="comment">; 取 base</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>, <span class="number">0xc0000000</span></span><br><span class="line">    <span class="keyword">mov</span> [gdt_ptr + <span class="number">2</span>], <span class="built_in">ebx</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">0xc0000000</span>                  <span class="comment">;将栈指针同样映射到内核地址</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, PAGE_DIR_TABLE_POS         <span class="comment">;把页目录地址赋给 cr3 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cr3</span>, <span class="built_in">eax</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="built_in">cr0</span>                        <span class="comment">;打开 cr0 的 pg 位（第 31 位）</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, <span class="number">0x80000000</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cr0</span>, <span class="built_in">eax</span> </span><br><span class="line">    </span><br><span class="line">    <span class="comment">;在开启分页后，用 gdt 新的地址重新加载</span></span><br><span class="line">    <span class="keyword">lgdt</span> [gdt_ptr]                          <span class="comment">;重新加载</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> <span class="built_in">dword</span> SELECTOR_CODE:enter_kernel    <span class="comment">; 刷新流水线,虽然已经是32位了，不用刷新，但还是添加上，防止出现莫名的问题</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">enter_kernel:</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">call</span> kernel_init            <span class="comment">;初始化内核</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esp</span>,<span class="number">0xc009f000</span>          <span class="comment">;新的栈起始地址0xc0000900</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;初始化gs寄存器，显存相关</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_VIDEO      <span class="comment">;这里要初始化gs寄存器，其实我尝试将它放在之前的位置初始化也可以打印成功好像</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">gs</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">160</span>], <span class="string">'M'</span>      <span class="comment">;视频段段基址已经被更新，用字符 M 表示 mouse 其实也是验证是否初始化成功</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> KERNEL_ENTRY_POINT      <span class="comment">;用地址 0x1500 访问测试，指为0xc0001500，也就是ld链接的虚拟地址的main函数</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">;-----------------------------创建页目录和页表---------------------------</span></span><br><span class="line"><span class="comment">; 页目录占用4kb，也就是4096字节(0x1000),PAGE_DIR_TABLE_POS是页目录的物理地址(0x100000)</span></span><br><span class="line"><span class="comment">;循环清零</span></span><br><span class="line"><span class="symbol">setup_page:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">4096</span>    </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="number">0</span>    <span class="comment">;初始化gs寄存器，显存相关</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_VIDEO      <span class="comment">;这里要初始化gs寄存器，其实我尝试将它放在之前的位置初始化也可以打印成功好像</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">gs</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">160</span>], <span class="string">'M'</span>      <span class="comment">;视频段段基址已经被更新，用字符 M 表示 mouse 其实也是验证真的执行没</span></span><br><span class="line"><span class="symbol">.clear_page_dir:</span>    <span class="comment">;清理页目录</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [PAGE_DIR_TABLE_POS + <span class="built_in">esi</span>],<span class="number">0</span>   <span class="comment">;清零</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span>                                 <span class="comment">;偏移一个字节,计数+1</span></span><br><span class="line">    <span class="keyword">loop</span> .clear_page_dir                    <span class="comment">;不断循环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;开始创建页目录(PDE)</span></span><br><span class="line"><span class="symbol">.creat_pde:</span>              <span class="comment">; 创建 Page Directory Entry</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>,<span class="number">0x1000</span>      <span class="comment">;此时的eax为第一个页表的位置和属性(偏移4kb)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,<span class="built_in">eax</span>         <span class="comment">;此处为ebx复制，是为了.creat_pte做准备，ebx为基地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 下面将页目录项 0 和 0xc00 都存为第一个页表的地址，每个页表表示 4MB 内存</span></span><br><span class="line"><span class="comment">; 这样 0xc03fffff 以下的地址和 0x003fffff 以下的地址都指向相同的页表</span></span><br><span class="line"><span class="comment">; 这是为将地址映射为内核地址做准备</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>,PG_US_U | PG_RW_W | PG_P         <span class="comment">;页目录项的属性 RW 和 P 位为 1，US 为 1，表示用户属性，所有特权级别都可以访问</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS + <span class="number">0X0</span>],<span class="built_in">eax</span>      <span class="comment">;第一个目录项，在页目录表中的第 1 个目录项写入第一个页表的位置(0x101000)及属性(7)</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS + <span class="number">0xc00</span>], <span class="built_in">eax</span>   <span class="comment">;0XC00是第768个页表占用的目录项(一个页表项占用4字节)，0xc00以上的目录项用于内核空间</span></span><br><span class="line"><span class="comment">; 也就是说页表的 0xc0000000～0xffffffff 共计 1G 属于内核</span></span><br><span class="line"><span class="comment">;               0x0～0xbfffffff 共计 3G 属于用户进程</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="built_in">eax</span>,<span class="number">0x1000</span>                      <span class="comment">;计算目录表自己的物理地址</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS+<span class="number">4092</span>],<span class="built_in">eax</span>   <span class="comment">;使最后一个目录项指向页目录表自己的地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;创建页表项(PTE)        </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">256</span>         <span class="comment">;1M 低端内存 / 每页大小 4k =256</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, <span class="number">0</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,PG_US_U | PG_RW_W |PG_P     <span class="comment">;属性为7，US=1,RW=1,P=1</span></span><br><span class="line"><span class="symbol">.creat_pte:</span>              <span class="comment">; 创建 Page Table Entry</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">esi</span>*<span class="number">4</span>],<span class="built_in">edx</span> <span class="comment">; 此时的edx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">4096</span>        <span class="comment">; 4kb</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span>             <span class="comment">; 计数+1</span></span><br><span class="line">    <span class="keyword">loop</span> .creat_pte     <span class="comment">; 循环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;创建内核其它页表的PDE</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, <span class="number">0x2000</span>                     <span class="comment">;此时eax为第二个页表的位置，偏移8kb</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, PG_US_U | PG_RW_W | PG_P    <span class="comment">;页目录项的属性 US､ RW 和 P 位都为 1</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">254</span>                        <span class="comment">;范围为第 769～1022 的所有目录项数量</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, <span class="number">769</span></span><br><span class="line"><span class="symbol">.create_kernel_pde:</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">esi</span>*<span class="number">4</span>],<span class="built_in">eax</span>                 </span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, <span class="number">0x1000</span></span><br><span class="line">    <span class="keyword">loop</span> .create_kernel_pde</span><br><span class="line">    <span class="keyword">ret</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------将kernel.bin中的 segment(段) 拷贝到编译的地址-----------------------</span></span><br><span class="line"><span class="symbol">kernel_init:</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">eax</span>, <span class="built_in">eax</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ebx</span>, <span class="built_in">ebx</span> <span class="comment">;ebx 记录程序头表地址</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ecx</span>, <span class="built_in">ecx</span> <span class="comment">;cx 记录程序头表中的 program header 数量</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">edx</span>, <span class="built_in">edx</span> <span class="comment">;dx 记录 program header 尺寸，即 e_phentsize</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,[KERNEL_BIN_BASE_ADDR +<span class="number">42</span>]   <span class="comment">;偏移42位的属性是e_phentsize，记录program header 大小</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,[KERNEL_BIN_BASE_ADDR +<span class="number">28</span>]  <span class="comment">;偏移28位记录的是e_phoff，表示第一个program header的偏移量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,KERNEL_BIN_BASE_ADDR</span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,[KERNEL_BIN_BASE_ADDR +<span class="number">44</span>]   <span class="comment">;偏移44位记录e_phum,表示有几个program header</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.each_segment:</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">byte</span> [<span class="built_in">ebx</span> + <span class="number">0</span>],PT_NULL      <span class="comment">;若p_type == PT_NULL,则program header未使用</span></span><br><span class="line">    <span class="keyword">je</span> .PTNULL                     <span class="comment">;则跳转到 .PT_NULL</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;函数 memcpy(dst,src,size)  为函数 memcpy 压入参数，参数是从右往左依然压入</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">dword</span> [<span class="built_in">ebx</span> + <span class="number">16</span>]           <span class="comment">;偏移16位记录p_filesz   是压入函数 memcpy 的第三个参数：size  </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, [<span class="built_in">ebx</span> + <span class="number">4</span>]              <span class="comment">;偏移4位记录p_offset   </span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, KERNEL_BIN_BASE_ADDR   <span class="comment">;加上 kernel.bin 被加载到的物理地址，eax 为该段的物理地址</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">eax</span>                        <span class="comment">;压入函数 memcpy 的第二个参数：源地址</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">dword</span> [<span class="built_in">ebx</span> + <span class="number">8</span>]            <span class="comment">;偏移8位记录p_vaddr     是压入函数 memcpy 的第一个参数：目的地址</span></span><br><span class="line">    <span class="keyword">call</span> mem_cpy                    <span class="comment">;调用 mem_cpy 完成段复制</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">12</span>                      <span class="comment">;清理栈中压入的三个参数</span></span><br><span class="line"><span class="symbol">.PTNULL:</span> </span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,<span class="built_in">edx</span>                     <span class="comment">;edx 为 program header 大小，即 e_phentsize,;在此 ebx 指向下一个 program header</span></span><br><span class="line">    <span class="keyword">loop</span> .each_segment</span><br><span class="line">    <span class="keyword">ret</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;-------------------------------------------------------------- </span></span><br><span class="line"><span class="comment">;功能:逐字节拷贝函数 mem_cpy（dst，src，size）</span></span><br><span class="line"><span class="comment">;输入:栈中三个参数（dst，src，size）</span></span><br><span class="line"><span class="comment">;输出:无</span></span><br><span class="line"><span class="comment">;---------------------------------------------------------------</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">mem_cpy:</span></span><br><span class="line">    <span class="keyword">cld</span>             <span class="comment">;清除方向标志（DF=0），确保字符串操作正向移动（esi/edi递增</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ebp</span>        <span class="comment">;保存调用者的栈基址 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebp</span>,<span class="built_in">esp</span>     <span class="comment">;建立新的栈帧（ebp指向当前栈顶）</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>        <span class="comment">;rep 指令用到了 ecx 但 ecx 对于外层段的循环还有用，所以先入栈备份</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edi</span>, [<span class="built_in">ebp</span> + <span class="number">8</span>]      <span class="comment">; 加载目标地址（dst） </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, [<span class="built_in">ebp</span> + <span class="number">12</span>]     <span class="comment">; 加载源地址（src）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, [<span class="built_in">ebp</span> + <span class="number">16</span>]     <span class="comment">; 加载要复制的字节数（size）</span></span><br><span class="line">    <span class="keyword">rep</span> <span class="keyword">movsb</span>               <span class="comment">; 逐字节拷贝,执行ecx次</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ecx</span>                 <span class="comment">; 恢复ecx</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ebp</span>                 <span class="comment">; 恢复调用者的栈基址</span></span><br><span class="line">    <span class="keyword">ret</span>                     <span class="comment">; 返回</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------------------------------</span></span><br><span class="line"><span class="comment">; 功能：在32位模式下读取硬盘的n个扇区</span></span><br><span class="line"><span class="comment">; 参数：</span></span><br><span class="line"><span class="comment">;   eax = LBA扇区号</span></span><br><span class="line"><span class="comment">;   ebx = 将数据写入的内存地址</span></span><br><span class="line"><span class="comment">;   ecx = 读入的扇区数</span></span><br><span class="line"><span class="comment">;----------------------------------------------------</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">rd_disk_m_32:</span></span><br><span class="line">    <span class="keyword">pushad</span>          <span class="comment">; 保存所有通用寄存器</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>        <span class="comment">; 保存ecx（扇区数）</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">eax</span>        <span class="comment">; 保存eax（LBA地址）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 第1步：设置读取的扇区数</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f2</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="built_in">cl</span>      <span class="comment">; 读取的扇区数</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 第2步：将LBA地址存入0x1f3 ~ 0x1f6</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">eax</span>         <span class="comment">; 恢复eax（LBA地址）</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">; LBA地址7～0位写入端口0x1f3</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f3</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">; LBA地址15～8位写入端口0x1f4</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>, <span class="number">8</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>, <span class="built_in">cl</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f4</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">; LBA地址23～16位写入端口0x1f5</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>, <span class="built_in">cl</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f5</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">; LBA地址24～27位（高4位）</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>, <span class="built_in">cl</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">al</span>, <span class="number">0x0f</span>    <span class="comment">; 只保留低4位</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">al</span>, <span class="number">0xe0</span>     <span class="comment">; 设置7～4位为1110，表示LBA模式</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f6</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 第3步：向0x1f7端口写入读命令0x20</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f7</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x20</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 第4步：检测硬盘状态</span></span><br><span class="line"><span class="symbol">.not_ready:</span></span><br><span class="line">    <span class="keyword">nop</span>             <span class="comment">; 空操作，延迟一小会</span></span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">al</span>, <span class="built_in">dx</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">al</span>, <span class="number">0x88</span>    <span class="comment">; 第4位为1表示准备好，第7位为1表示硬盘忙</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">al</span>, <span class="number">0x08</span></span><br><span class="line">    <span class="keyword">jnz</span> .not_ready  <span class="comment">; 若未准备好，继续等待</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 第5步：从0x1f0端口读取数据</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ecx</span>         <span class="comment">; 恢复ecx（扇区数）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f0</span></span><br><span class="line"><span class="symbol">.read_sector:</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>        <span class="comment">; 保存剩余扇区数</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">256</span>    <span class="comment">; 每个扇区256次inw操作（512字节/2字节）</span></span><br><span class="line"><span class="symbol">.read_word:</span></span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">ax</span>, <span class="built_in">dx</span>       <span class="comment">; 从端口读取一个字（2字节）</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>], <span class="built_in">ax</span>   <span class="comment">; 存储到内存</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>, <span class="number">2</span>      <span class="comment">; 内存地址增加2字节</span></span><br><span class="line">    <span class="keyword">loop</span> .read_word <span class="comment">; 循环读取整个扇区</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ecx</span>         <span class="comment">; 恢复剩余扇区数</span></span><br><span class="line">    <span class="keyword">loop</span> .read_sector <span class="comment">; 读取下一个扇区</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">popad</span>           <span class="comment">; 恢复所有通用寄存器</span></span><br><span class="line">    <span class="keyword">ret</span></span><br></pre></td></tr></table></figure></div>              </div>            </details><p>最后就是验证是否成功执行loader，首先我将之前的字符<code>V</code>更改成了<code>M</code>，大家可以在屏幕上看到，其次是如何观察是否进入了main函数<br>这里验证方式有很多，比如说往一个特定地址写入一个特殊值，然后我们通过bochs调试读取，或者通过操作显存来显示字符，我这里选择后者，所以稍微修改一下main函数:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">volatile</span> <span class="type">char</span> *video = (<span class="keyword">volatile</span> <span class="type">char</span> *)<span class="number">0xB8000</span>;</span><br><span class="line">    <span class="type">char</span> *str = <span class="string">"Mouse OS"</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> attribute = <span class="number">0x07</span>; <span class="comment">//灰字黑底</span></span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; str[i] != <span class="string">'\0'</span>; i++) <span class="comment">// 从屏幕左上角开始依次写入每个字符及其属性</span></span><br><span class="line">    {</span><br><span class="line">        video[i * <span class="number">2</span>] = str[i];      </span><br><span class="line">        video[i * <span class="number">2</span> + <span class="number">1</span>] = attribute; </span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>); <span class="comment">// 防止 main 退出</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后重新编译写入即可，然后就可以在屏幕的第一行看见字符串”Mouse OS”</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sh loader_mbr_start.sh</span><br><span class="line">sh kernel/kernel_start.sh</span><br></pre></td></tr></table></figure></div><p>加载内核就到此结束，下面来看看保护模式下最闪亮的内容—特权</p><hr><h2 id="C-特权级的深入浅出"><a href="#C-特权级的深入浅出" class="headerlink" title="C. 特权级的深入浅出"></a>C. 特权级的深入浅出</h2><p>CPU 既是大脑，又是警察，它负责维护计算机内的安全。它将程序拥有的权利分为 4 个等级，这就是保护模式下特权级的由来。<br>特权级按照权力从大到小分为 0、1、2、3 级，没错，数字越小，权力越大，0 级特权能力最大，3 级特权能力最小。<strong>0 级特权是我们操作系统内核所在的特权级</strong>，计算机在启动之初就以 0 级特权运行</p><hr><h3 id="C-1-TSS-简介"><a href="#C-1-TSS-简介" class="headerlink" title="C.1 TSS 简介"></a>C.1 TSS 简介</h3><p>TSS，即 Task State Segment，意为任务状态段，它是处理器在硬件上原生支持多任务的一种实现方式,TSS 是一种数据结构，它用于存储任务的环境</p><blockquote><p>TSS 是硬件支持的系统数据结构，它和 GDT 等一样，由软件填写其内容，由硬件使用。GDT 也要加载到寄存器 GDTR 中才能被处理器找到，TSS 也是一样，它是由 TR（Task Register）寄存器加载的，每次处理器执行不同任务时，将 TR 寄存器加载不同任务的 TSS 就成了。至于怎么加载以及相关工作原理，目前咱们用不到，还是放在后面说比较合适。</p></blockquote><p>正是由于处理器提供了硬件方面的框架，所以很多工作都是“自动”完成的，虽然操作系统看上去是底层的技术，但其实也属于“应用型”开发。</p><hr><h3 id="C-2-CPL-和-DPL-简介"><a href="#C-2-CPL-和-DPL-简介" class="headerlink" title="C.2 CPL 和 DPL 简介"></a>C.2 CPL 和 DPL 简介</h3><ul><li>CPL</li></ul><blockquote><p>前情提要，x86 访问内存的机制是“段基址：偏移地址”，无论是实模式，还是保护模式，都要遵循此方式，在实模式下，段基址直接写在段寄存器中，而在保护模式下，段寄存器中的不再是段基址，而是段选择子，通过该选择子从 GDT 或 LDT 中找到相应的段描述符，从该描述符中获取段的起始地址。</p></blockquote><blockquote><p>代码段描述符中的 DPL，便是当前 CPU 所处的特权级，这个特权级称为当前特权级，即 CPL（Current Privilege Level），它表示处理器正在执行的代码的特权级别。除一致性代码段外，转移后的目标代码据段的 DPL 是将来处理器的当前特权级 CPL。</p></blockquote><blockquote><p>总之，代码是资源的请求者，代码段寄存器 CS 所指向的是处理器中当前运行的指令，所以代码段寄存器 CS 中选择子的 RPL 位称为当前特权级 CPL</p></blockquote><blockquote><p>处理器的当前特权级 CPL 放在 CS.RPL 中</p></blockquote><ul><li>DPL</li></ul><blockquote><p>DPL，即 Descriptor Privilege Level，描述符特权级，这下您清楚为什么 DPL 字段在段描述符中占 2位的原因了吧，两位能表示 4 个组合，00b、01b、10b、11b，所有特权级都齐了</p></blockquote><p>/略……</p><hr><h3 id="C-3-ps-略一下…-等我具体理解了在补充这一部分"><a href="#C-3-ps-略一下…-等我具体理解了在补充这一部分" class="headerlink" title="C.3 ps: 略一下….等我具体理解了在补充这一部分"></a>C.3 ps: 略一下….等我具体理解了在补充这一部分</h3><p>这里书中还介绍了<strong>调用门</strong>,<strong>RPL 的前世今生</strong>以及<strong>IO 特权级</strong>,这里还是主要记录与代码强相关的内容，所以暂且少写一点，详情可以翻阅书</p><hr><h2 id="D-完善内核–打印功能"><a href="#D-完善内核–打印功能" class="headerlink" title="D. 完善内核–打印功能"></a>D. 完善内核–打印功能</h2><p>之前终于完成内核，但是似乎还是太简陋了，只是实现了打印字符串，所以这里开始，完善内核</p><hr><h3 id="D-1-函数调用约定简介"><a href="#D-1-函数调用约定简介" class="headerlink" title="D.1 函数调用约定简介"></a>D.1 函数调用约定简介</h3><p>因为我们需要汇编和C语言结合编程，所以如果互相调用了怎么办，这里主要了解一下函数调用规约</p><p>这里还是通过书中的例子来帮助理解:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">fun1</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> a+b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> a =<span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b =<span class="number">2</span>;</span><br><span class="line">    <span class="type">int</span> c = fun1(<span class="number">1</span>,<span class="number">2</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>上面就是一个我们常见的用法，但是计算机有事如何来确认参数1,2是在哪嘞,这是一个参数存储问题</p><ul><li><strong>首先，每个进程都有自己的栈，这就是每个内存自己的专用内存空间</strong></li><li><strong>其次，保存参数的内存地址不用再花精力维护，已经有栈机制来维护地址变化了，参数在栈中的位置可以通过栈顶的偏移量来得到</strong></li></ul><p>参数存储的问题解决了，我们决定在进程自己的栈空间中保存参数，那<strong>调用函数后谁负责回收所占用的栈空间</strong>，同时如果<strong>参数很多的时候，我们又该以什么顺序传递</strong></p><p>在c语言中，我们不用考虑这个问题，因为编译器默默承受了这些事情，我们只用负责使用</p><p>而在汇编中，我们写一个函数，再调用一下，自己写的当然可以知道顺序，比如我们之前的mem_cpy函数，我们只用根据自己设置好的顺序，来按顺序调用即可:</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> <span class="built_in">edi</span>, [<span class="built_in">ebp</span> + <span class="number">8</span>]      <span class="comment">; 加载目标地址（dst） </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">esi</span>, [<span class="built_in">ebp</span> + <span class="number">12</span>]     <span class="comment">; 加载源地址（src）</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ecx</span>, [<span class="built_in">ebp</span> + <span class="number">16</span>]     <span class="comment">; 加载要复制的字节数（size）</span></span><br></pre></td></tr></table></figure></div><p>但是假设函数并不是你自己写的，那么我们就需要双方提前商量好传入参数的顺序和由谁来负责清理栈空间<br><strong>在高级语言中，这两个问题是通过调用约定来解决的，调用约定就是调用方和被调用方就以上问题达成一致解决方案的约定，双方按照这种约定合作就不会发生问题</strong></p><p>因为<strong>C 语言遵循的调用约定是 cdecl，所以我们自然要遵守 cdecl 约定</strong>，下面主要介绍一下cdecl约定：</p><hr><ul><li><strong>cdecl约定</strong></li></ul><blockquote><p><strong>cdecl 调用约定最大的亮点是它允许函数中参数的数量不固定的</strong>，我们熟识的 printf 函数，它能够支持变长参数，就是利用此 cdecl 调用约定的性质设计出来的，它的原理是利用字符串参数 format 中的’%’来匹配栈中的参数，以后咱们在动手实现 printf 函数时会体验到这一优势</p></blockquote><p>用之前的<code>int fun1(int a,int b)</code>函数来举个例子</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">fun1</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> a+b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> c = fun1(<span class="number">1</span>,<span class="number">2</span>)</span><br></pre></td></tr></table></figure></div><blockquote><p>主调用者</p></blockquote><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;从右向左将参数入栈</span></span><br><span class="line"><span class="keyword">push</span> <span class="number">2</span>          <span class="comment">;压入参数 b </span></span><br><span class="line"><span class="keyword">push</span> <span class="number">1</span>          <span class="comment">;压入参数 a</span></span><br><span class="line"><span class="keyword">call</span> fun1       <span class="comment">;调用函数 fun1 </span></span><br><span class="line"><span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">8</span>       <span class="comment">;回收（清理）栈空间</span></span><br></pre></td></tr></table></figure></div><blockquote><p>被调用者</p></blockquote><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">push</span> <span class="built_in">ebp</span>                <span class="comment">;压入 ebp 备份</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ebp</span>,<span class="built_in">esp</span>             <span class="comment">;将 esp 赋值给 ebp </span></span><br><span class="line">                        <span class="comment">;用 ebp 作为基址来访问栈中参数</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>,[<span class="built_in">ebp</span>+<span class="number">0x8</span>]       <span class="comment">;偏移 8 字节处为第 1 个参数 a </span></span><br><span class="line"><span class="keyword">add</span> <span class="built_in">eax</span>,[<span class="built_in">ebp</span>+<span class="number">0xc</span>]       <span class="comment">;偏移 0xc 字节处是第 2 个参数 b </span></span><br><span class="line">                        <span class="comment">;参数 a 和 b 相加后存入 eax </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">esp</span>,<span class="built_in">ebp</span>             <span class="comment">;为防止中间有入栈操作，用 ebp 恢复 esp </span></span><br><span class="line">                        <span class="comment">;本句在此例子中可有可无,属于通用代码</span></span><br><span class="line"><span class="keyword">pop</span> <span class="built_in">ebp</span>                 <span class="comment">;将 ebp 恢复</span></span><br><span class="line"><span class="keyword">ret</span></span><br></pre></td></tr></table></figure></div><p>ok呀，大概就这样就实现了双方的约定，下面来看看如何混合编程</p><hr><h3 id="D-2-函汇编语言和-C-语言混合编程"><a href="#D-2-函汇编语言和-C-语言混合编程" class="headerlink" title="D.2 函汇编语言和 C 语言混合编程"></a>D.2 函汇编语言和 C 语言混合编程</h3><p>开门见山，汇编语言和 C 语言混合编程可分为两大类。</p><ul><li><strong>单独的汇编代码文件与单独的 C 语言文件分别编译成目标文件后，一起链接成可执行程序。</strong></li><li><strong>在 C 语言中嵌入汇编代码，直接编译生成可执行程序。</strong></li></ul><p>我们这里主要使用第一种，分开编程，第二种又称为内联汇编，在一些长汇编确实不适合，这个后面会专门说明</p><p>大概了解一下什么是Linux系统调用：</p><blockquote><p>系统调用是 Linux 内核提供的一套子程序，它和 Windows 的动态链接库 dll 文件的功能一样，用来实现一系列在用户态不能或不易实现的功能，比如最常见的读写硬盘文件，只有操作系统有权限去访问硬件，用户程序是没有权限的，用户程序只能向操作系统寻求帮助，故系统调用是供用户程序来使用的，操作系统权利至高无上，不需要使用自己对外发布的功能接口，即系统调用</p></blockquote><p>系统调用的入口只有一个，即第 0x80 号中断 为什么系统调用只有一个入口呢？<br><strong>中断的实现是要用到中断描述符表</strong>，表中很多中断项（号）是被预留的，不能强占，所以 Linux 就选了一个可用的中断号作为所有系统调用的统一入口，具体的子功能在寄存器 eax 中单独指定</p><blockquote><p>BIOS 中断走的是中断向量表，所以有很多中断号给它用，而系统调用走的是中断描述符表中<br>的一项而已，所以只用了第 0x80 项中断</p></blockquote><p>这里书中有个<strong>系统调用write</strong>的例子，大家如果感兴趣可以去看看，我这里就不写出来了</p><hr><p><strong>这里说说C语言和汇编如何互相调用</strong>,举个例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**** /home/mouse/OS_mouse/tool/bochs/mouse/drafts/c_with_s_c.c  ****/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="type">void</span> <span class="title function_">asm_print</span><span class="params">(<span class="type">char</span>*,<span class="type">int</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">c_print</span><span class="params">(<span class="type">char</span>* str)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (str[len])  <span class="comment">/* 遇到 '\0' 停止 */</span></span><br><span class="line">        len++;</span><br><span class="line">    asm_print(str, len);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;/home/mouse/OS_mouse/tool/bochs/mouse/drafts/c_with_s_s.S</span></span><br><span class="line"><span class="meta">section</span> .data </span><br><span class="line"><span class="symbol">str:</span> <span class="built_in">db</span> <span class="string">"asm_print says hello world!"</span>, <span class="number">0xa</span>, <span class="number">0</span> </span><br><span class="line"><span class="comment">;0xa 是换行符,0 是手工加上的字符串结束符\0 的 ASCII 码</span></span><br><span class="line">str_len <span class="built_in">equ</span> $-<span class="keyword">str</span> </span><br><span class="line"><span class="meta">section</span> .text </span><br><span class="line"><span class="meta">extern</span> c_print      <span class="comment">;声明c文件的函数</span></span><br><span class="line"><span class="meta">global</span> _start </span><br><span class="line"><span class="symbol">_start:</span> </span><br><span class="line"><span class="comment">;;;;;;;;;;;; 调用 c 代码中的函数 c_print ;;;;;;;;;;; </span></span><br><span class="line">    <span class="keyword">push</span> <span class="keyword">str</span>        <span class="comment">;传入参数</span></span><br><span class="line">    <span class="keyword">call</span> c_print    <span class="comment">;调用 c 函数</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">4</span>       <span class="comment">;回收栈空间</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;;;;;;;;;;;;;;;;;;; 退出程序 ;;;;;;;;;;;;;;;;;;;; </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,<span class="number">1</span>   <span class="comment">;第 1 号子功能是 exit 系统调用</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x80</span>    <span class="comment">;发起中断，通知 Linux 完成请求的功能</span></span><br><span class="line"></span><br><span class="line"><span class="meta">global</span> asm_print <span class="comment">;相当于 asm_print（str,size）</span></span><br><span class="line"><span class="symbol">asm_print:</span> </span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ebp</span>            <span class="comment">;备份 ebp </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebp</span>,<span class="built_in">esp</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,<span class="number">4</span>           <span class="comment">;第 4 号子功能是 write 系统调用</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, <span class="number">1</span>          <span class="comment">;此项固定为文件描述符 1，标准输出（stdout）指向屏幕</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, [<span class="built_in">ebp</span>+<span class="number">8</span>]    <span class="comment">;第 1 个参数</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>, [<span class="built_in">ebp</span>+<span class="number">12</span>]   <span class="comment">;第 2 个参数</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x80</span>            <span class="comment">;发起中断，通知 Linux 完成请求的功能</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ebp</span>             <span class="comment">;恢复 ebp </span></span><br><span class="line">    <span class="keyword">ret</span></span><br></pre></td></tr></table></figure></div><p>汇编中的<code>__start</code>执行，然后调用c文件的<code>c_print</code>函数，然后<code>c_print</code>函数再调用汇编的<code>asm_print</code>函数，然后再进行系统调用(0x80中断的4功能)，然后输出到屏幕上</p><ul><li><strong>在汇编代码中导出符号供外部引用是用的关键字 <code>global</code>，引用外部文件的符号是用的关键字<code>extern</code></strong></li><li><strong>在 C 代码中只要将符号定义为全局便可以被外部引用（一般情况下无需用额外关键字修饰，具体请参考 C 语言手册），引用外部符号时用 <code>extern</code> 声明即可</strong></li></ul><p>下面我们编译链接试试，验证一下结果:</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#32位，注意就是在自己终端上运行就行，64位兼容32位，直接用就行</span></span><br><span class="line">gcc -m32 -c c_with_s_c.c -o c_with_s_c.o</span><br><span class="line">nasm -f elf c_with_s_s.S -o c_with_s_s.o <span class="comment">#指定elf格式</span></span><br><span class="line"><span class="comment"># 链接两个目标文件，生成最终的可执行程序</span></span><br><span class="line">ld -m elf_i386 c_with_s_c.o c_with_s_s.o -o final_program_32</span><br><span class="line"></span><br><span class="line"><span class="built_in">chmod</span> 777 final_program_32      <span class="comment">#添加权限，为了方便我就直接给777了</span></span><br><span class="line">./final_program_32              <span class="comment">#运行</span></span><br></pre></td></tr></table></figure></div><p>最后可以看到屏幕输出<code>asm_print says hello world!</code>,证明成功了</p><hr><h3 id="D-3实现自己的打印函数"><a href="#D-3实现自己的打印函数" class="headerlink" title="D.3实现自己的打印函数"></a>D.3实现自己的打印函数</h3><blockquote><p>一直以来，我们在往屏幕上输出文本时，要么利用 BIOS 中断，要么利用系统调用，这些都是依赖别人的方法。咱们还用过一个稍微有点独立的方法，就是直接写显存，但这貌似又没什么技术含量。如今我们要写一个打印函数了</p></blockquote><p>所以下面来和显卡打打交道</p><ul><li><strong>显卡的端口控制</strong></li></ul><p>首先呀，显卡肯定不只能打印文本(mov 一些ASCII码写进去)，这是因为我们在默认的文本模式下。显卡当然还有显示各种绚丽的图片，这都是显卡的功能，但目前我们还是在学习步骤，所以还是在80*25的文本模式下操作即可</p><p>下面书中介绍了显卡的具体的寄存器表，建议大家去翻书或者网上查看，这里就不列出了</p><p>这里还是直接通过例子来操作:</p><ul><li><strong>实现单个字符打印</strong></li></ul><p>在此之前，为了开发方便，我们要学习linux内核一样定义一些数据类型，首先创建一个目录lib，然后再添加两个目录<code>kernel</code> <code>user</code>分别是内核和用户使用的库文件，然后添加一个<code>stdint.h</code>文件</p><p>然后打印函数在<code>/lib/kernel/print.S</code>中实现,其中<code>put_char</code>函数就是我们等会要实现的函数(与汇编打交道当然纯汇编比较方便),当然，为了方便查看文件路径，我在每个示例代码的最上面都会添加文件所在的路径，方便查看</p><p><strong>下面是这个打印函数的核心处理流程</strong>:</p><blockquote><p>（1）备份寄存器现场。<br>（2）获取光标坐标值，光标坐标值是下一个可打印字符的位置。<br>（3）获取待打印的字符。<br>（4）判断字符是否为控制字符，若是回车符、换行符、退格符三种控制字符之一，则进入&gt;相应的处理<br>流程。否则，其余字符都被粗暴地认为是可见字符，进入输出流程处理。<br>（5）判断是否需要滚屏。<br>（6）更新光标坐标值，使其指向下一个打印字符的位置。<br>（7）恢复寄存器现场，退出。</p></blockquote><p>书中将这个实现分成了三部分，因为我都把注释写到代码里了，所以这里代码直接展示完整的吧</p><ul><li>备份，然后获取光标位置，获取字符，判断应该怎么处理，然后跳转到相应的函数</li><li>完善对应的函数</li><li>判断滚屏，更新光标位置，恢复现场并退出</li></ul><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/print.S</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------定义视频段的选择子---------------------------------</span></span><br><span class="line"><span class="comment">;一般要放在配置文件中，这里偷懒一下(因为只有三行)</span></span><br><span class="line">TI_GDT  <span class="built_in">equ</span>     <span class="number">0</span>   <span class="comment">;描述符表指示符​：0：使用全局描述符表；1：则表示使用局部描述符表​ </span></span><br><span class="line">RPL0    <span class="built_in">equ</span>     <span class="number">0</span>   <span class="comment">;请求特权级​：表示选择子的特权级。0是最高特权级（操作系统内核），还有1、2、3（用户通常为3）</span></span><br><span class="line">SELECTOR_VIDEO  <span class="built_in">equ</span> (<span class="number">0X0003</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">;​段选择子​：一个具体的值，用于在GDT中索引一个段描述符(0x0003表示全局描述符表的第四个描述符，也就是显存段的)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------- put_char -----------------------</span></span><br><span class="line"><span class="comment">;功能: 将栈中的一个字符写入光标所在处</span></span><br><span class="line"><span class="comment">;--------------------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="meta">global</span> put_char     <span class="comment">;声明为外部可调用</span></span><br><span class="line"><span class="symbol">put_char:</span></span><br><span class="line">    <span class="keyword">pushad</span>      <span class="comment">;备份32位寄存器环境 push all double,指压入所有双字节长的寄存器(32个字节)</span></span><br><span class="line">                <span class="comment">;需保证gs为正确的视频段选择子，为保险，每次打印都为其赋值</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,SELECTOR_VIDEO       <span class="comment">;不能直接将立即数送入段寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">gs</span>,<span class="built_in">ax</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;------------------------------ 获取当前光标位置 ------------------------------</span></span><br><span class="line">    <span class="comment">;先或获得高八位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x03d4</span>   <span class="comment">;索引寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>,<span class="number">0x0e</span>     <span class="comment">;用于提供光标位置的高八位</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span>       <span class="comment">;将索引 0xe 写入索引寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x03d5</span>   <span class="comment">;通过读写0x3d5来获得或设置光标位置</span></span><br><span class="line">    <span class="keyword">in</span>  <span class="built_in">al</span>,<span class="built_in">dx</span>       <span class="comment">;得到了光标位置的高八位,读入al</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="number">ah</span>,<span class="built_in">al</span>       <span class="comment">;在写入ah寄存器(in指令要求源操作数和目标数必须一样都是8位/16位，所以这里多移动一次)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;再获取低 8 位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0f</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span> </span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">al</span>, <span class="built_in">dx</span> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>,<span class="built_in">ax</span>       <span class="comment">;将光标存入 bx(常用于bx位基址寻址)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;--------------------- 获取待打印字符并判断类型-&gt;跳转相关函数  --------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span> ,[<span class="built_in">esp</span>+<span class="number">36</span>]   <span class="comment">;返回地址占 4 字节,pushad占 32 字节 所以共36字节，然后就可以获得待打印的字符</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>,<span class="number">0xd</span>      <span class="comment">;CR(回车)是0x0d</span></span><br><span class="line">    <span class="keyword">jz</span> .is_carriage_return</span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>, <span class="number">0xa</span>     <span class="comment">;LF(换行)是0x0a，这里二者都处理成回车换行(CRLF)</span></span><br><span class="line">    <span class="keyword">jz</span> .is_line_feed</span><br><span class="line"></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>,<span class="number">0x8</span>      <span class="comment">;BS(backspace 退格)</span></span><br><span class="line">    <span class="keyword">jz</span> .is_backspace</span><br><span class="line">    <span class="keyword">jmp</span> .put_other  <span class="comment">;其余字符</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;--------------------------------- 相关函数的实现  -------------------------------</span></span><br><span class="line"><span class="comment">;----- 退格 -----</span></span><br><span class="line"><span class="symbol">.is_backspace:</span></span><br><span class="line">    <span class="comment">;这里退格之后，还添加了空格或者空字符0，这样就会覆盖后面的字符</span></span><br><span class="line">    <span class="keyword">dec</span> <span class="built_in">bx</span>      <span class="comment">;bx--(光标--)</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">bx</span>,<span class="number">1</span>    <span class="comment">;bx&lt;&lt;1(相当于乘2,即转换为显存中的字节偏移量)</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>],<span class="number">0x20</span>   <span class="comment">;填充字节为0或者空格(0x20是空格)</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">bx</span>                  <span class="comment">;bx++ 写入属性</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>], <span class="number">0x07</span>  <span class="comment">;黑底白字</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">bx</span>,<span class="number">1</span>                <span class="comment">;bx&gt;&gt;1,恢复光标值</span></span><br><span class="line">    <span class="keyword">jmp</span> .set_cursor         <span class="comment">;更新光标位置</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 其它字符 -----</span></span><br><span class="line"><span class="symbol">.put_other:</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">bx</span>,<span class="number">1</span>    <span class="comment">;bx&lt;&lt;1(相当于乘2,即转换为显存中的字节偏移量)</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>],<span class="built_in">cl</span>     <span class="comment">;填充字节为0或者空格(0x20是空格)</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">bx</span>                  <span class="comment">;bx++ 写入属性</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="built_in">bx</span>], <span class="number">0x07</span>  <span class="comment">;黑底白字</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">bx</span>,<span class="number">1</span>                <span class="comment">;bx&gt;&gt;1,恢复光标值</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">bx</span>                  <span class="comment">;bx++,下一个光标值</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">bx</span>, <span class="number">2000</span>            <span class="comment">;如果小于2000，表示未写到显存的末尾，则去设置光标值</span></span><br><span class="line">                            <span class="comment">;如果超过2000，表示需要换行</span></span><br><span class="line">    <span class="keyword">jl</span> .set_cursor         <span class="comment">;更新光标位置（如果小于2000）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 回车换行 -----</span></span><br><span class="line"><span class="symbol">.is_line_feed:</span>              <span class="comment">;换行(\n)</span></span><br><span class="line"><span class="symbol">.is_carriage_return:</span>        <span class="comment">;回车(\r) 光标移动到行首，这里统一都写成回车换行</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">dx</span>,<span class="built_in">dx</span>               <span class="comment">;dx是被除数的高16位，清零</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">bx</span>               <span class="comment">;ax是被除数的低16位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">si</span>,<span class="number">80</span>               <span class="comment">;每行是80字符</span></span><br><span class="line">    <span class="keyword">div</span> <span class="built_in">si</span>                  <span class="comment">;计算行位置</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="built_in">bx</span>,<span class="built_in">dx</span>               <span class="comment">;光标值减去除 80 的余数便是取整</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.is_carriage_return_end:</span>    <span class="comment">;回车符处理结束</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">bx</span>,<span class="number">80</span>               <span class="comment">;光标移动到下一行</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">bx</span>,<span class="number">2000</span>             <span class="comment">;检查是否超出屏幕范围(80x25=2000)</span></span><br><span class="line"><span class="symbol">.is_line_feed_end:</span></span><br><span class="line">    <span class="keyword">jl</span> .set_cursor          <span class="comment">;如果bx&lt;2000则跳转到设置光标位置</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 滚屏 -----  </span></span><br><span class="line"><span class="comment">;这里有两种方案，第一种是是要设置起始地址寄存器，优点是可以缓存16KB个字符，屏幕外的文本也可以很快的找回</span></span><br><span class="line"><span class="comment">;               第二种是固定屏幕，,直接搬运，优点是不用设置起始地址寄存器，缺点是只能缓存2000个字符</span></span><br><span class="line"><span class="comment">;这里书中介绍的是第二种，因为简单方便，易于理解</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;第二种滚屏方法:</span></span><br><span class="line"><span class="comment">;1.将第 1～24 行的内容整块搬到第 0～23 行，也就是把第 0 行的数据覆盖。</span></span><br><span class="line"><span class="comment">;2.再将第 24 行，也就是最后一行的字符用空格覆盖，这样它看上去是一个新的空行。</span></span><br><span class="line"><span class="comment">;3.把光标移到第 24 行也就是最后一行行首</span></span><br><span class="line"><span class="comment">;搬运1~24行</span></span><br><span class="line"><span class="symbol">.roll_screen:</span>       <span class="comment">;若超出屏幕大小，开始滚屏</span></span><br><span class="line">    <span class="keyword">cld</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">960</span>     <span class="comment">;要搬运2000-80个字符，1920*2=3840字节</span></span><br><span class="line">                    <span class="comment">;一次搬运4字节，也就是960次</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="number">0xc00b80a0</span>  <span class="comment">;第1行行首---复制起始地址</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edi</span>,<span class="number">0xc00b8000</span>  <span class="comment">;第0行行首---复制目标地址</span></span><br><span class="line">    <span class="keyword">rep</span> <span class="keyword">movsd</span>           <span class="comment">;逐4字节复制(32)</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, <span class="number">3840</span>       <span class="comment">;最后一行首字符的第一个字节的偏移1920*2</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">80</span>         <span class="comment">;一行80个字符，一次一个字符(2字节)，要移动80次</span></span><br><span class="line"><span class="comment">;清理最后一行</span></span><br><span class="line"><span class="symbol">.cls:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">word</span> [<span class="built_in">gs</span>:<span class="built_in">ebx</span>],<span class="number">0x0720</span>    <span class="comment">;0x0720是黑底白字的空格键</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,<span class="number">2</span>           <span class="comment">;一次一个字符(字+属性)</span></span><br><span class="line">    <span class="keyword">loop</span> .cls           <span class="comment">;循环清理80次</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>,<span class="number">1920</span>         <span class="comment">;设置光标为1920,也就是最后一行的首字符   </span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----- 设置光标 ----- </span></span><br><span class="line"><span class="symbol">.set_cursor:</span></span><br><span class="line"><span class="comment">;将光标设置为bx值</span></span><br><span class="line">    <span class="comment">;---- 设置高8位 ----</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span>  <span class="comment">;索引寄存器    </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0e</span>    <span class="comment">;用于提供光标位置的高 8 位</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span>  <span class="comment">;通过读写数据端口 0x3d5 来获得或设置光标位置</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">bh</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line">    <span class="comment">;---- 设置低8位 ----</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d4</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="number">0x0f</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x03d5</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>, <span class="built_in">bl</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>, <span class="built_in">al</span> </span><br><span class="line"><span class="comment">;函数结尾</span></span><br><span class="line"><span class="symbol">.put_char_done:</span> </span><br><span class="line">    <span class="keyword">popad</span>   <span class="comment">;恢复寄存器环境</span></span><br><span class="line">    <span class="keyword">ret</span>     <span class="comment">;返回</span></span><br></pre></td></tr></table></figure></div><p>然后是外部调用肯定是引用头文件更加方便，所以同样创建一个头文件</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/print.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_KERNEL_PRINT_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_KERNEL_PRINT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">put_char</span><span class="params">(<span class="type">uint8_t</span> char_asci)</span>;   <span class="comment">//打印一个字符</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>然后就是主函数了,这里选择打印一个长字符串，注意要一个字符一个字符打印，因为我们还没实现字符串的功能</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_char(<span class="string">'M'</span>);</span><br><span class="line">    put_char(<span class="string">'o'</span>);</span><br><span class="line">    put_char(<span class="string">'u'</span>);</span><br><span class="line">    put_char(<span class="string">'s'</span>);</span><br><span class="line">    put_char(<span class="string">'e'</span>);</span><br><span class="line">    put_char(<span class="string">' '</span>);</span><br><span class="line">    put_char(<span class="string">'p'</span>);</span><br><span class="line">    put_char(<span class="string">'u'</span>);</span><br><span class="line">    put_char(<span class="string">'t'</span>);</span><br><span class="line">    put_char(<span class="string">'c'</span>);</span><br><span class="line">    put_char(<span class="string">'h'</span>);</span><br><span class="line">    put_char(<span class="string">'a'</span>);</span><br><span class="line">    put_char(<span class="string">'r'</span>); </span><br><span class="line">    put_char(<span class="string">'\r'</span>);</span><br><span class="line">    put_char(<span class="string">'\n'</span>);</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);   <span class="comment">//防止退出</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最后编译链接启动，这里我修改一下脚本，方便一键启动，当然后面肯定会用makefile，因为shell脚本还是不够方便</p><div class="code-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel_start.sh</span></span><br><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">BASE_DIR=<span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse/kernel"</span>     <span class="comment">#内核文件</span></span><br><span class="line">LIB_DIR=<span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel"</span>  <span class="comment">#内核库函数路径</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> <span class="variable">$BASE_DIR</span>    <span class="comment">#进入相关路径</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译主文件和外部文件</span></span><br><span class="line">gcc -m32 -I<span class="variable">$LIB_DIR</span> -c -o main.o main.c</span><br><span class="line">nasm -f elf -o print.o <span class="variable">$LIB_DIR</span>/print.S</span><br><span class="line"></span><br><span class="line"><span class="comment"># 链接所有目标文件</span></span><br><span class="line">ld -m elf_i386 main.o print.o -Ttext 0xc0001500 -e main -o kernel.bin</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入磁盘映像</span></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=<span class="variable">$BASE_DIR</span>/kernel.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc</span><br></pre></td></tr></table></figure></div><p>在虚拟机运行bochs之后就可以看到屏幕上多了一个字符串 <code>Mosue putchar</code>,那么证明我们写的代码没有问题，并且发现光标在这个字符串的后两行(因为我们输入了\r\n，所以跳转了两次回车换行)，也符合我们的代码</p><hr><p>那么下面就来实现打印字符串吧，这里直接将<code>put_str</code>函数添加到<code>put_char</code>函数之前即可，因为前面有，就不贴完整代码了:</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;略......</span></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line"><span class="comment">;----------------------------- put_str -----------------------</span></span><br><span class="line"><span class="comment">;功能: 通过put_char打印以0字符结尾的字符串</span></span><br><span class="line"><span class="comment">;输入:栈中参数为打印的字符串</span></span><br><span class="line"><span class="comment">;输出:无</span></span><br><span class="line"><span class="comment">;--------------------------------------------------------------</span></span><br><span class="line"><span class="meta">global</span> put_str      <span class="comment">;声明为外部可调用</span></span><br><span class="line"><span class="symbol">put_str:</span></span><br><span class="line">    <span class="comment">;由于本函数中只用到了 ebx 和 ecx，只备份这两个寄存器</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ebx</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ecx</span>,<span class="built_in">ecx</span>         <span class="comment">;清零</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,[<span class="built_in">esp</span>+<span class="number">12</span>]    <span class="comment">;备份的2个寄存器+返回地址(8+2)</span></span><br><span class="line"><span class="symbol">.goon:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>,[<span class="built_in">ebx</span>]</span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>,<span class="number">0</span>    <span class="comment">;如果处理到了字符串末尾('0')，则跳转到结束处返回 </span></span><br><span class="line">    <span class="keyword">jz</span> .str_over</span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>    <span class="comment">;为put_char函数传递参数</span></span><br><span class="line">    <span class="keyword">call</span> put_char</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">4</span>   <span class="comment">;回收参数所占用的空间</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">ebx</span>     <span class="comment">;让ebx指向下一个字符</span></span><br><span class="line">    <span class="keyword">jmp</span> .goon   <span class="comment">;循环判断字符串直到末尾</span></span><br><span class="line"><span class="symbol">.str_over:</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ebx</span>     <span class="comment">;还原寄存器</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ecx</span>     </span><br><span class="line">    <span class="keyword">ret</span>         <span class="comment">;返回</span></span><br><span class="line"><span class="comment">;----------------------------- put_char -----------------------</span></span><br><span class="line"><span class="comment">;功能: 将栈中的一个字符写入光标所在处</span></span><br><span class="line"><span class="comment">;--------------------------------------------------------------</span></span><br><span class="line"><span class="comment">;略......</span></span><br></pre></td></tr></table></figure></div><p>然后就是添加头文件：</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/print.h</span></span><br><span class="line"><span class="comment">/****略****/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">put_str</span><span class="params">(<span class="type">char</span>* essage)</span>;        <span class="comment">//打印一个字符串</span></span><br><span class="line"><span class="comment">/****略****/</span></span><br></pre></td></tr></table></figure></div><p>最后修改一下main.c，然后就可以执行了:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"Mouse_put_str\r\b"</span>);</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);   <span class="comment">//防止退出</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mouse@ubuntu:~/OS_mouse/tool/bochs/mouse/kernel$ sh kernel_start.sh </span><br><span class="line"><span class="comment">#记录了5+1 的读入</span></span><br><span class="line"><span class="comment">#记录了5+1 的写出</span></span><br><span class="line"><span class="comment">#2916 bytes (2.9 kB, 2.8 KiB) copied, 0.000288471 s, 10.1 MB/s</span></span><br></pre></td></tr></table></figure></div><p>然后运行之后，如果再屏幕上看见字符串<code>Mouse_put_str</code>，并且光标在字符串这一行的末尾则现象正确(先换行，然后又退了一格)</p><hr><p>最后一步，实现打印整数(pot_int)，为最后的<code>pirntf</code>实现打基础，同时这里结束后也要学习什么是内联汇编(C和汇编联合编译)</p><p>下面还是给出需要添加的代码:</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/print.S</span></span><br><span class="line"><span class="comment">;略......................</span></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line">put_int_buffer <span class="built_in">dq</span> <span class="number">0</span> <span class="comment">; 定义 8 字节缓冲区用于数字到字符的转换</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------------- put_int -----------------------</span></span><br><span class="line"><span class="comment">;功能: 将小端字节序的数字变成对应的 ASCII 后，倒置</span></span><br><span class="line"><span class="comment">;输入:栈中参数为待打印的数字</span></span><br><span class="line"><span class="comment">;输出:在屏幕上打印十六进制数字，并不会打印前缀 0x</span></span><br><span class="line"><span class="comment">;     如打印十进制 15 时，只会直接打印 f，不会是 0xf</span></span><br><span class="line"><span class="comment">;--------------------------------------------------------------</span></span><br><span class="line"><span class="meta">global</span> put_int      <span class="comment">;声明为外部可调用</span></span><br><span class="line"><span class="symbol">put_int:</span></span><br><span class="line">    <span class="keyword">pushad</span>          <span class="comment">; 保存所有通用寄存器状态</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebp</span>, <span class="built_in">esp</span>    <span class="comment">; 设置基址指针为当前栈顶</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, [<span class="built_in">ebp</span>+<span class="number">4</span> * <span class="number">9</span>]  <span class="comment">; 获取栈中参数（返回地址4字节 + pushad的8个4字节）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>, <span class="built_in">eax</span>    <span class="comment">; 复制参数到EDX用于处理</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edi</span>, <span class="number">7</span>      <span class="comment">; 设置缓冲区起始偏移（指向最高字节位置）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">8</span>      <span class="comment">; 循环计数器：32位数字对应8个十六进制位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, put_int_buffer <span class="comment">; EBX指向转换缓冲区基地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 将32位数字按十六进制从低位到高位处理</span></span><br><span class="line"><span class="symbol">.16based_4bits:</span>     <span class="comment">; 处理每个4位十六进制数字</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">edx</span>, <span class="number">0x0000000F</span>  <span class="comment">; 取当前最低4位</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edx</span>, <span class="number">9</span>      <span class="comment">; 判断是数字(0-9)还是字母(A-F)</span></span><br><span class="line">    <span class="keyword">jg</span> .is_A2F      <span class="comment">; 大于9则为字母</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>, <span class="string">'0'</span>    <span class="comment">; 转换为数字字符ASCII</span></span><br><span class="line">    <span class="keyword">jmp</span> .store</span><br><span class="line"><span class="symbol">.is_A2F:</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="built_in">edx</span>, <span class="number">10</span>     <span class="comment">; 计算字母偏移量（A-F对应10-15）</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>, <span class="string">'A'</span>    <span class="comment">; 转换为字母字符ASCII</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 将字符存储到缓冲区（高位字符在低地址）</span></span><br><span class="line"><span class="symbol">.store:</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">edi</span>], <span class="built_in">dl</span>   <span class="comment">; 存储转换后的字符</span></span><br><span class="line">    <span class="keyword">dec</span> <span class="built_in">edi</span>         <span class="comment">; 前移缓冲区位置（向低地址）</span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>, <span class="number">4</span>      <span class="comment">; 右移4位处理下一个十六进制位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>, <span class="built_in">eax</span>    <span class="comment">; 更新EDX为剩余未处理部分</span></span><br><span class="line">    <span class="keyword">loop</span> .16based_4bits <span class="comment">; 循环处理所有8个十六进制位</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 准备打印：跳过高位连续的'0'</span></span><br><span class="line"><span class="symbol">.ready_to_print:</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">edi</span>         <span class="comment">; 调整EDI到第一个字符位置（之前为-1）</span></span><br><span class="line"><span class="symbol">.skip_prefix_0:</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edi</span>, <span class="number">8</span>      <span class="comment">; 检查是否已处理完所有字符</span></span><br><span class="line">    <span class="keyword">je</span> .full0       <span class="comment">; 如果全是0则跳转到全0处理</span></span><br><span class="line"><span class="symbol">.go_on_skip:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>, [put_int_buffer+<span class="built_in">edi</span>] <span class="comment">; 读取当前字符</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">edi</span>         <span class="comment">; 指向下一个字符</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">cl</span>, <span class="string">'0'</span>     <span class="comment">; 检查是否为'0'</span></span><br><span class="line">    <span class="keyword">je</span> .skip_prefix_0 <span class="comment">; 如果是'0'则继续跳过</span></span><br><span class="line">    <span class="keyword">dec</span> <span class="built_in">edi</span>         <span class="comment">; 回退到第一个非0字符位置</span></span><br><span class="line">    <span class="keyword">jmp</span> .put_each_num <span class="comment">; 开始打印非0部分</span></span><br><span class="line"><span class="symbol">.full0:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>, <span class="string">'0'</span>     <span class="comment">; 全0情况只打印单个'0'</span></span><br><span class="line"><span class="symbol">.put_each_num:</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ecx</span>        <span class="comment">; 压栈字符参数供put_char使用</span></span><br><span class="line">    <span class="keyword">call</span> put_char   <span class="comment">; 调用字符打印函数</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>, <span class="number">4</span>      <span class="comment">; 清理栈空间</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">edi</span>         <span class="comment">; 指向缓冲区下一个字符</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>, [put_int_buffer+<span class="built_in">edi</span>] <span class="comment">; 读取下一个字符</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edi</span>, <span class="number">8</span>      <span class="comment">; 检查是否已打印所有字符</span></span><br><span class="line">    <span class="keyword">jl</span> .put_each_num <span class="comment">; 继续打印直到结束</span></span><br><span class="line">    <span class="keyword">popad</span>           <span class="comment">; 恢复所有通用寄存器</span></span><br><span class="line">    <span class="keyword">ret</span>             <span class="comment">; 函数返回</span></span><br><span class="line"><span class="comment">;略......................</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ;/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel/print.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_KERNEL_PRINT_H </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_KERNEL_PRINT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">put_char</span><span class="params">(<span class="type">uint8_t</span> char_asci)</span>;   <span class="comment">//打印一个字符</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">put_str</span><span class="params">(<span class="type">char</span>* essage)</span>;         <span class="comment">//打印一个字符串</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">put_int</span><span class="params">(<span class="type">uint32_t</span> num)</span>;         <span class="comment">//打印一个整数，16进制</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"Mouse_put_str\r\b"</span>);</span><br><span class="line">    put_int(<span class="number">1</span>);</span><br><span class="line">    put_str(<span class="string">"  "</span>);</span><br><span class="line">    put_int(<span class="number">12</span>);</span><br><span class="line">    put_int(<span class="number">0x0000f</span>);</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);   <span class="comment">//防止退出</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后还是执行sh脚本，屏幕上应该会有<code>1  CF</code>显示，那么说明整数打印也没问题啦</p><hr><h3 id="D-4-内联汇编简介"><a href="#D-4-内联汇编简介" class="headerlink" title="D.4 内联汇编简介"></a>D.4 内联汇编简介</h3><p>之前介绍过了一种汇编方法，就是 C 代码和汇编代码分别编译，最后通过链接的方式结合在<br>一起形成可执行文件。<br>另一种方式就是在 C 代码中直接嵌入汇编语言，这就是内联汇编</p><p>这里主要列出内联汇编的部分知识点，具体使用可以参考例子，或者网上查阅相关资料</p><h4 id="D4-1-汇编语言-AT-T-语法简介"><a href="#D4-1-汇编语言-AT-T-语法简介" class="headerlink" title="D4.1 汇编语言 AT&T 语法简介"></a>D4.1 汇编语言 AT&amp;T 语法简介</h4><p>我们在大学所学习的汇编语言大多数都是 Intel 语法,Linux 内核中的汇编代码一般都<br>是 AT&amp;T 语法，下面列出表格指出主要的不同点:</p><table><thead><tr><th align="left">特性</th><th align="left">AT&amp;T 语法</th><th align="left">Intel 语法</th></tr></thead><tbody><tr><td align="left"><strong>操作数顺序</strong></td><td align="left"><code>opcode src, dest</code></td><td align="left"><code>opcode dest, src</code></td></tr><tr><td align="left"><strong>寄存器前缀</strong></td><td align="left"><code>%</code> (例如 <code>%eax</code>)</td><td align="left">无前缀 (例如 <code>eax</code>)</td></tr><tr><td align="left"><strong>立即数前缀</strong></td><td align="left"><code>$</code> (例如 <code>$0x80</code>)</td><td align="left">无前缀 (例如 <code>0x80</code> 或 <code>80h</code>)</td></tr><tr><td align="left"><strong>内存操作数</strong></td><td align="left"><code>disp(base, index, scale)</code></td><td align="left"><code>[base + index*scale + disp]</code></td></tr><tr><td align="left"></td><td align="left">(例如 <code>4(%ebx)</code>)</td><td align="left">(例如 <code>[ebx+4]</code>)</td></tr><tr><td align="left"><strong>操作码后缀</strong></td><td align="left">使用后缀指明大小</td><td align="left">在内存操作数前加修饰</td></tr><tr><td align="left"></td><td align="left">(例如 <code>b</code>-字节, <code>w</code>-字, <code>l</code>-双字)</td><td align="left">(例如 <code>byte ptr</code>)</td></tr><tr><td align="left"><strong>远调用/跳转</strong></td><td align="left"><code>lcall</code>, <code>ljmp</code></td><td align="left"><code>call far</code>, <code>jmp far</code></td></tr></tbody></table><h4 id="D4-2-基本内联汇编"><a href="#D4-2-基本内联汇编" class="headerlink" title="D4.2 基本内联汇编"></a>D4.2 基本内联汇编</h4><p>基本内联汇编是最简单的内联形式,格式为:<br><code>asm [volatile] ("assembly code")</code> 其中voliatile的意思是不要修改我写的汇编代码,防止编译器优化</p><blockquote><p>“assembly code”是咱们所写的汇编代码，它必须位于圆括号中，而且必须用双引号引起来。这&gt;是格式要求，只要满足了这个格式 asm [volatile] (“”)，assembly code 甚至可以为空。<br>下面说下 assembly code 的规则。<br>（1）指令必须用双引号引起来，无论双引号中是一条指令或多条指令。<br>（2）一对双引号不能跨行，如果跨行需要在结尾用反斜杠’'转义。<br>（3）指令之间用分号’；’、换行符’\n’或换行符加制表符’\n’’\t’分隔。<br>提醒一下，即使是指令分布在多个双引号中，gcc 最终也要把它们合并到一起来处理，合并之后，指令间必须要有分隔符。所以，当指令在多个双引号中时，除最后一个双引号外，其余双引号中的代码最后一定要有分隔符，这和其他编程语言中表示代码结束的分隔符是一样的，如：</p></blockquote><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">asm(<span class="string">"movl $9,%eax;"</span><span class="string">"pushl %eax"</span>) 正确</span><br><span class="line">asm(<span class="string">"movl $9,%eax"</span><span class="string">"pushl %eax"</span>) 错误</span><br></pre></td></tr></table></figure></div><p>在<strong>内联汇编中，咱们要注意操作数的顺序啦，现在是和 Intel 反着的</strong></p><p>下面举一个完整的演示例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// home/mouse/OS_mouse/tool/bochs/mouse/drafts/inlineASM.c</span></span><br><span class="line"><span class="comment">//调用系统调用 write 打印机字符串</span></span><br><span class="line"><span class="type">char</span> * str=<span class="string">"hello,world\n"</span>;</span><br><span class="line"><span class="type">int</span> count =<span class="number">0</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">asm</span>(<span class="string">"pusha;         \</span></span><br><span class="line"><span class="string">        movl $4,%eax;   \</span></span><br><span class="line"><span class="string">        movl $1,%ebx;   \</span></span><br><span class="line"><span class="string">        movl str,%ecx;  \</span></span><br><span class="line"><span class="string">        movl $12,%edx;  \</span></span><br><span class="line"><span class="string">        int $0x80;      \</span></span><br><span class="line"><span class="string">        mov %eax,count; \</span></span><br><span class="line"><span class="string">        popa            \</span></span><br><span class="line"><span class="string">        "</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后编译运行，便可以在中断看到打印的<code>hello,world</code></p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gcc -m32  -o inlineASM.bin inlineASM.c </span><br><span class="line">./inlineASM.bin </span><br></pre></td></tr></table></figure></div><h4 id="D4-2-拓展内联汇编"><a href="#D4-2-拓展内联汇编" class="headerlink" title="D4.2 拓展内联汇编"></a>D4.2 拓展内联汇编</h4><p>很明显，之前的基础内联汇编并没有使用c语言里面的变量，而在实际中，使用变量是不可避免的，所以这里内联汇编的格式也发生了变化：<br><code>asm [volatile] (“assembly code”:output : input : clobber/modify)</code></p><blockquote><p>和前面的基本内联汇编相比，扩展内联汇编在圆括号中变成了 4 部分，多了 output、input 和clobber/modify 三项。其中的每一部分都可以省略，甚至包括 assembly code。省略的部分要保留冒号分隔符来占位，如果省略的是后面的一个或多个连续的部分，分隔符也不用保留，比如省略了 clobber/modify，不需要保留 input 后面的冒号。</p></blockquote><p><strong>output:output</strong> 用来指定汇编代码的数据如何输出给 C 代码使用<br>格式: <code>“操作数修饰符约束名”（C 变量名）</code><br><strong>input：input</strong> 用来指定 C 中数据如何输入给汇编使用<br>格式: <code>“[操作数修饰符] 约束名”（C 变量名）</code><br><strong>clobber/modify</strong>：汇编代码执行后会破坏一些内存或寄存器资源，通过此项通知编译器，可能造成寄存器或内存数据的破坏，这样 gcc 就知道哪些寄存器或内存需要提前保护起来</p><p>这里还是直接举个例子，更加容易理解:<br><strong>分别是参数约束和内存约束</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// home/mouse/OS_mouse/tool/bochs/mouse/drafts/inlineASM.c</span></span><br><span class="line"><span class="comment">// 拓展内联汇编</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span> </span></span><br><span class="line"><span class="type">void</span> <span class="title function_">main</span><span class="params">()</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">//参数约束:两数相加</span></span><br><span class="line">    <span class="type">int</span> in_a = <span class="number">1</span>, in_b = <span class="number">2</span>, out_sum; </span><br><span class="line">    <span class="keyword">asm</span>(<span class="string">"addl %%ebx, %%eax"</span>:<span class="string">"=a"</span>(out_sum):<span class="string">"a"</span>(in_a),<span class="string">"b"</span>(in_b)); </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"sum is %d\n"</span>,out_sum); </span><br><span class="line"></span><br><span class="line">    <span class="comment">//内存约束：变量 in_b 用 in_a 的值替换。in_b 最终变成 1。</span></span><br><span class="line">    in_a = <span class="number">1</span>, in_b = <span class="number">2</span>; </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"in_b is %d\n"</span>,in_b); </span><br><span class="line">    <span class="keyword">asm</span>(<span class="string">"movb %b0, %1;"</span>::<span class="string">"a"</span>(in_a),<span class="string">"m"</span>(in_b)); </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"in_b now is %d\n"</span>, in_b);</span><br><span class="line">} </span><br></pre></td></tr></table></figure></div><p>然后还是编译输出查看结果:</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gcc -m32  -o reg_constraint.bin reg_constraint.c</span><br><span class="line">./reg_constraint.bin</span><br></pre></td></tr></table></figure></div><p>屏幕上如果输出如下，那么就成功运行啦<br><code>sum is 3</code><br><code>in_b is 2</code><br><code>in_b now is 1</code></p><p>下面来说说什么是占位符，<strong>占位符分为序号占位符和名称占位符两种</strong><br>这里还是举几个例子，就不细说概念了</p><p><code>asm("addl %%ebx, %%eax":"=a"(out_sum):"a"(in_a),"b"(in_b));</code><br>等价于<br><code>asm("addl %2, %1":"=a"(out_sum):"a"(in_a),"b"(in_b));</code></p><p>其中<br><code>"=a"(out_sum)</code>序号为<code>0</code>，<code>%0</code>对应的是<code>eax</code><br><code>"a"(in_a)</code>序号为<code>1</code>，<code>%1</code>对应的是<code>eax</code><br><code>"b"(in_b)</code>序号为<code>2</code>，<code>%2</code>对应的是<code>ebx</code></p><p>下面还有名称占位符，等用到的时候我再加解释，大家可以自行去书中了解</p><hr><h2 id="E-中断"><a href="#E-中断" class="headerlink" title="E. 中断"></a>E. 中断</h2><p>在学习中断之前，我提前来规划一下以后编译使用的Makefile,因为以后的文件肯定会慢慢增多，所以再用以前的指令或者sh脚本肯定不方便，那肯定有专门的文件来干这个事吧，那就是Makefile文件了</p><hr><h3 id="E-1-Makefile"><a href="#E-1-Makefile" class="headerlink" title="E.1 Makefile"></a>E.1 Makefile</h3><p>这里书中是学到内存管理才介绍的，但为了编译文件方便，这里提前介绍一下，并创建我们的makefile文件</p><p>emm,至于语法和概念大家自行查找吧，因为具体内容很多地方都会讲，比如之前学习imx6u也会说明，这里列出后面会使用的文件内容，方便一会写中断等文件后编译</p><p>这里我选择分层级创建makefile文件，也就是在内核的文件夹下创建一个，然后分别在<code>/lib/kernel</code>和<code>/lib/user</code>等路径下都添加一个(如果没有，可以参考我给的路径创建一个，后期会用到)，以后如果添加文件就只用在对应的目录下进行操作即可，这里大部分我都写了注释，大家可以参考参考……</p><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  内核构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/kernel/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># .PHONY 标明伪目标,它代表了一个需要被执行的动作或任务，而非一个需要被生成的文件</span></span><br><span class="line"><span class="comment"># 可以在本文件所在目录下 使用make all 、 make clean 等命令执行一系列任务</span></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: all kernel user clean img dirs</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 基础路径  =======================================</span></span><br><span class="line"><span class="comment"># 其中的 ":=" 表示立即展开使用，即右边的变量值会被立刻赋值，"$" 可以引用之前创建的变量</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line">MOUSE_DIR := ..</span><br><span class="line">BUILD_DIR := <span class="variable">$(MOUSE_DIR)</span>/build</span><br><span class="line">BUILD_KERNEL_DIR := <span class="variable">$(BUILD_DIR)</span>/kernel</span><br><span class="line">BUILD_USER_DIR := <span class="variable">$(BUILD_DIR)</span>/user</span><br><span class="line">IMG_PATH := /home/mouse/OS_mouse/tool/bochs/hd60M.img</span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================ 编译工具  =======================================</span></span><br><span class="line">CC := gcc</span><br><span class="line">ASM := nasm</span><br><span class="line">LD := ld</span><br><span class="line"></span><br><span class="line"><span class="comment"># C编译器标志，这里指出输出32位，编译器不识别/使用内建函数，禁用栈保护机制，指定当前为独立式环境(标准库不存在)，</span></span><br><span class="line"><span class="comment"># -I 分别包含需要的头文件路径(共用库，内核库，用户库，内核，设备)</span></span><br><span class="line">CFLAGS := -m32 -fno-builtin -fno-stack-protector -ffreestanding \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/lib -I<span class="variable">$(MOUSE_DIR)</span>/lib/kernel -I<span class="variable">$(MOUSE_DIR)</span>/lib/user \</span><br><span class="line">          -I<span class="variable">$(MOUSE_DIR)</span>/kernel -I<span class="variable">$(MOUSE_DIR)</span>/device -c</span><br><span class="line">ASMFLAGS := -f elf</span><br><span class="line">LDFLAGS := -m elf_i386 -Ttext 0xc0001500 -e main</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===================== 包含子Makefile，导入子模块源文件  ===============================</span></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/device/Makefile</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(MOUSE_DIR)</span>/lib/Makefile</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============================== 本目录源文件  ===========================================</span></span><br><span class="line">KERNEL_SRCS := main.c init.c interrupt.c debug.c memory.c</span><br><span class="line">KERNEL_ASMS := kernel.S</span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 为各模块添加前缀路径  =======================================</span></span><br><span class="line"><span class="comment"># $(addprefix &lt;prefix&gt;,&lt;names&gt;)是一个内置函数，能将路径prefix添加到names前</span></span><br><span class="line"><span class="comment"># ======================================================================================</span></span><br><span class="line">KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_SRCS)</span>)</span></span><br><span class="line">KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/kernel/,<span class="variable">$(KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_KERNEL_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_SRCS)</span>)</span></span><br><span class="line">LIB_KERNEL_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/kernel/,<span class="variable">$(LIB_KERNEL_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_USER_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_SRCS)</span>)</span></span><br><span class="line">LIB_USER_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/,<span class="variable">$(LIB_USER_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">DEVICE_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_SRCS)</span>)</span></span><br><span class="line">DEVICE_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/device/,<span class="variable">$(DEVICE_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line">LIB_SRCS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_SRCS)</span>)</span></span><br><span class="line">LIB_ASMS := <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(MOUSE_DIR)</span>/lib/,<span class="variable">$(LIB_ASMS)</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 汇总全部源文件  ================================================</span></span><br><span class="line">ALL_C_SRCS := <span class="variable">$(KERNEL_SRCS)</span> <span class="variable">$(LIB_KERNEL_SRCS)</span> <span class="variable">$(LIB_USER_SRCS)</span> <span class="variable">$(DEVICE_SRCS)</span> <span class="variable">$(LIB_SRCS)</span></span><br><span class="line">ALL_ASMS   := <span class="variable">$(KERNEL_ASMS)</span> <span class="variable">$(LIB_KERNEL_ASMS)</span> <span class="variable">$(LIB_USER_ASMS)</span> <span class="variable">$(DEVICE_ASMS)</span> <span class="variable">$(LIB_ASMS)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ========================== 生成对应的 .o 文件路径  ========================================</span></span><br><span class="line"><span class="comment"># $(filter &lt;pattern...&gt;,&lt;text&gt;)：这个函数用于从 &lt;text&gt;中筛选出符合模式 &lt;pattern&gt;的单词</span></span><br><span class="line"><span class="comment"># $(patsubst &lt;pattern&gt;,&lt;replacement&gt;,&lt;text&gt;)：将 &lt;text&gt;中所有匹配 &lt;pattern&gt;的单词替换为 </span></span><br><span class="line"><span class="comment"># &lt;replacement&gt;的形式，&lt;pattern&gt;中可以使用通配符 %</span></span><br><span class="line"><span class="comment"># 通过这个函数，将生成的.o文件分别生成到build路径下的不同路径(这里除了用户，都生成到/build/kernel)</span></span><br><span class="line"><span class="comment"># =========================================================================================</span></span><br><span class="line">OBJS_KERNEL := \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.c,<span class="variable">$(ALL_C_SRCS)</span>)</span>) \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,<span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/%.S,<span class="variable">$(ALL_ASMS)</span>)</span>)</span><br><span class="line"></span><br><span class="line">OBJS_USER := \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c,<span class="variable">$(BUILD_USER_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c,<span class="variable">$(ALL_C_SRCS)</span>)</span>) \</span><br><span class="line">  <span class="variable">$(<span class="built_in">patsubst</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S,<span class="variable">$(BUILD_USER_DIR)</span>/%.o,$(<span class="built_in">filter</span> <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S,<span class="variable">$(ALL_ASMS)</span>)</span>)</span><br><span class="line"></span><br><span class="line">KERNEL_BIN := <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line"></span><br><span class="line"><span class="comment"># ==================================== 主要规则  ==========================================</span></span><br><span class="line"><span class="section">all: dirs kernel user</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 以@开头的指令，终端只会显示命令的输出，不显示命令本身--这里创建build文件夹</span></span><br><span class="line"><span class="section">dirs:</span></span><br><span class="line">@echo <span class="string">"Creating build directories..."</span></span><br><span class="line">@mkdir -p <span class="variable">$(BUILD_KERNEL_DIR)</span> <span class="variable">$(BUILD_USER_DIR)</span></span><br><span class="line"></span><br><span class="line"><span class="section">kernel: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line"><span class="section">user: <span class="variable">$(OBJS_USER)</span></span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(KERNEL_BIN)</span>: <span class="variable">$(OBJS_KERNEL)</span> <span class="variable">$(OBJS_USER)</span></span><br><span class="line">@echo <span class="string">"Linking kernel object files to generate kernel.bin..."</span></span><br><span class="line"><span class="variable">$(LD)</span> <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o,<span class="variable">$^</span>)</span> <span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(BUILD_USER_DIR)</span>/%.o,<span class="variable">$^</span>)</span></span><br><span class="line">@echo <span class="string">"Kernel linking completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================= 编译规则 ============================================</span></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_KERNEL_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.c | dirs</span><br><span class="line">@echo <span class="string">"Compiling user C: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_USER_DIR)</span>/%.o: <span class="variable">$(MOUSE_DIR)</span>/lib/user/%.S | dirs</span><br><span class="line">@echo <span class="string">"Assembling user S: <span class="variable">$&lt;</span>"</span></span><br><span class="line">@mkdir -p $(@D)</span><br><span class="line"><span class="variable">$(ASM)</span> <span class="variable">$(ASMFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$&lt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 镜像生成 ==========================================</span></span><br><span class="line"><span class="section">img: <span class="variable">$(KERNEL_BIN)</span></span></span><br><span class="line">@echo <span class="string">"Writing kernel image to disk..."</span></span><br><span class="line">dd if=<span class="variable">$(KERNEL_BIN)</span> of=<span class="variable">$(IMG_PATH)</span> bs=512 count=200 seek=9 conv=notrunc</span><br><span class="line">@echo <span class="string">"Kernel image writing completed!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ================================== 清理规则 ==========================================</span></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">@echo <span class="string">"Cleaning build files..."</span></span><br><span class="line">-rm -f <span class="variable">$(BUILD_KERNEL_DIR)</span>/*.o <span class="variable">$(BUILD_KERNEL_DIR)</span>/kernel.bin</span><br><span class="line">-rm -f <span class="variable">$(BUILD_USER_DIR)</span>/*.o</span><br><span class="line">@echo <span class="string">"Cleanup completed!"</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  用户库构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/lib/user/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line">LIB_USER_DIR := lib/user</span><br><span class="line">LIB_USER_SRCS :=</span><br><span class="line">LIB_USER_ASMS :=</span><br><span class="line">LIB_USER_INC := <span class="variable">$(LIB_USER_DIR)</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  内核库构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/kernel/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line">LIB_KERNEL_DIR := lib/kernel</span><br><span class="line">LIB_KERNEL_ASMS := print.S </span><br><span class="line">LIB_KERNEL_SRCS :=</span><br><span class="line">LIB_KERNEL_INC := <span class="variable">$(LIB_KERNEL_DIR)</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  Device驱动构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/device/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line">DEVICE_ASMS :=  <span class="comment">#timer.S</span></span><br><span class="line">DEVICE_ASMS += </span><br><span class="line"></span><br><span class="line">DEVICE_SRCS += </span><br><span class="line"></span><br><span class="line">DEVICE_INC := <span class="variable">$(DEVICE_DIR)</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="Makefile"><figure class="iseeu highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"><span class="comment">#    Makefile  Lib通用库构建文件 /home/mouse/OS_mouse/tool/bochs/mouse/lib/Makefile</span></span><br><span class="line"><span class="comment"># =================================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 通用库汇编源文件定义</span></span><br><span class="line">LIB_ASMS :=  <span class="comment"># 暂无汇编文件</span></span><br><span class="line"><span class="comment"># LIB_ASMS += example.S  # 如需添加汇编文件，在此处取消注释并添加</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 通用库C源文件定义</span></span><br><span class="line">LIB_SRCS := <span class="comment">#string.c    # 字符串处理函数</span></span><br><span class="line">LIB_SRCS += <span class="comment"># 这里添加文件</span></span><br><span class="line"><span class="comment"># LIB_SRCS += stdio.c    # 标准IO函数（如需添加取消注释）</span></span><br><span class="line"><span class="comment"># LIB_SRCS += memory.c   # 内存操作函数（如需添加取消注释）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 头文件目录路径</span></span><br><span class="line">LIB_INC := <span class="variable">$(LIB_DIR)</span></span><br></pre></td></tr></table></figure></div><p>这样以后如果要添加编译的内核库文件，就只用在对应的子目录的 Makefile 里面添加文件即可(注意在主文件夹添加文件也要修改Makefile，因为没有使用自动扫描)<br>同时这里的编译为相对路径，只有写入磁盘是绝对路径，这样即便你把文件发送给别人，那么也可以直接使用<br>为了目录简洁，这里还引入一个新的目录build，将以后编译生成的文件都放到这里面</p><p>Makefile就先说到这里，下面继续学习中断</p><hr><h3 id="E-2-中断分类"><a href="#E-2-中断分类" class="headerlink" title="E.2 中断分类"></a>E.2 中断分类</h3><p>这里的中断和之前stm32，或者说linux中断都是差不多的,这里还是简要说明，同时，这里全部都是以单核CPU为例子</p><hr><ul><li><strong>外部中断</strong></li></ul><ol><li>INTR(INTeRrupt):<strong>可屏蔽中断</strong>，CPU收到信号后可以缓慢处理，甚至不处理，因为它并不会影响CPU运行，同时也可以通过eflags寄存器的 IF 位 将这些中断全部屏蔽，对于这些每个中断源都可以获得一个中断向量号(有限的)</li><li>NMI(Non Maskable Interrupt):<strong>不可屏蔽中断</strong>，说明发生了致命的错误，比如内存读写错误，电源掉电，总线奇偶校验失误等等，并且只会被分配一个中断向量号，因为其实软件工程师也解决不了这些问题</li></ol><p>同时，这里简要说明一下中断的上半部和下半部，其实可以理解成上半部是表示得到中断信息（<strong>获得标志位</strong>）了，但是如果要处理的话可能会花费一点时间，这个时候如果有其它重要的事情也要处理的话，可能会影响别人的进程，所以这里将具体处理的部分可以放到下半部（<strong>执行</strong>）</p><hr><ul><li><strong>内部中断</strong></li></ul><ol><li><p><strong>软中断</strong>,就是由软件主动发起的中断，由于该中断是软件运行中主动发起的，所以它是主观上的，并不是客观上的某种内部错误</p></li><li><p><strong>异常</strong>，是指令执行期间 CPU 内部产生的错误引起的，由于是运行时错误，所以它不受标志寄存器 eflags 中的 IF 位影响，无法向用户隐瞒，只要中断关系到“正常”运行，就不受 IF 位影响，这里依照错误的轻重程度，分为三种:Fault(故障)，Trap(陷阱)，Abort(终止)</p></li></ol><p>下面是发起中断的相关指令:除了第一个指令外，都可以称作异常，因为它们既具备软中断的“主动”行为，又具备异常的“错误”结果</p><blockquote><ul><li><code>int 8 位立即数</code>,进行系统调用，可以表示256(8位)种中断;</li><li><code>int3</code>,注意这里面没有空格，这里指的是调试断点指令，其所触发的中断向量号是3</li><li><code>into</code>,中断溢出指令,所触发的中断向量号是 4,能否引发 4 号中断是要看 eflags 标志寄存器中的 OF 位是否为 1，如果是 1 才会引发中断，否则该指令悄悄地什么都不做</li><li><code>bound</code>,检查数组索引越界指令,触发 5 号中断,该指令格式是<code>bound 16/32 位寄存器，16/32 位内存</code>,目的操作数是用寄存器来存储的，其内容是待检测的数组下标值。源操作数是内存，其内容是数组下标的下边界和上边界。当执行 bound 指令时，若下标处于数组索引的范围之外，则会触发 5 号中断</li><li><code>ud2</code>，未定义指令，这会触发第 6 号中断，该指令表示指令无效，CPU 无法识别。主动使用它发起中断，常用于软件测试中，无实际用途</li></ul></blockquote><hr><blockquote><p>中断机制的本质是来了一个中断信号后，调用相应的中断处理程序。所以，CPU 不管有多少种类型的中断，为了统一中断管理，把来自外部设备、内部指令的各种中断类型统统归结为一种管理方式，即为每个中断信号分配一个整数，用此整数作为中断的 ID，而这个整数就是所谓的中断向量，然后用此 ID 作为中断描述符表中的索引，这样就能找到对应的表项，进而从中找到对应的中断处理程序。中断向量的作用和选择子类似，它们都用来在描述符表中索引一个描述符，只不过选择子用于在 GDT或 LDT 中检索段描述符，而中断向量专用于中断描述符表，其中没有 RPL 字段</p></blockquote><p><strong>异常和不可屏蔽中断的中断向量号是由 CPU 自动提供</strong>的，而来自外部设备的<strong>可屏蔽中断号是由中断代理提供的（咱们这里的中断代理是 8259A）</strong>，<strong>软中断是由软件提供</strong>的</p><hr><h3 id="E-3-中断描述符表"><a href="#E-3-中断描述符表" class="headerlink" title="E.3 中断描述符表"></a>E.3 中断描述符表</h3><p><strong>中断描述符表（Interrupt Descriptor Table，IDT）是保护模式下用于存储中断处理程序入口的表</strong><br>实模式下用于存储中断处理程序入口的表叫中断向量表（Interrupt Vector Table，IVT）,stm32也是中断向量表</p><p>同时，中断描述符表里面不仅仅有中断描述符，还有门描述符:</p><ul><li><p><strong>任务门</strong>:任务门和任务状态段（Task Status Segment，TSS）是 Intel 处理器在硬件一级提供的任务切换机制，所以任务门需要和 TSS 配合在一起使用，在任务门中记录的是 TSS 选择子，偏移量未使用,但是<strong>大多数操作系统都未使用TSS，所以这里不多介绍</strong></p></li><li><p><strong>中断门</strong>:中断门包含了<strong>中断处理程序所在段的段选择子和段内偏移地址</strong>。当通过此方式<strong>进入中断后，标志寄存器 eflags 中的 IF 位自动置 0</strong>，也就是在进入中断后，自动把中断关闭，<strong>避免中断嵌套</strong>。Linux 就是利用中断门实现的系统调用，就是那个著名的 int 0x80。</p></li><li><p><strong>陷阱门</strong>:陷阱门和中断门非常相似，区别是由<strong>陷阱门进入中断后，标志寄存器 eflags 中的 IF 位不会自动置 0</strong>。</p></li><li><p><strong>调用门</strong>:<strong>调用门是提供给用户进程进入特权 0 级的方式</strong>，它不能用int 指令调用，只能用 call 和 jmp 指令</p></li></ul><p>对比中断向量表，中断描述符表有两个区别:<br>（1）中断描述符表地址不限制，在哪里都可以,而中断向量表固定特殊地址(比如Flash存储器的起始位置)<br>（2）中断描述符表中的每个描述符用 8 字节描述</p><hr><h3 id="E-4-中断处理过程及保护"><a href="#E-4-中断处理过程及保护" class="headerlink" title="E.4 中断处理过程及保护"></a>E.4 中断处理过程及保护</h3><p>保护也就是特权级检查，完整的中断过程分为 CPU 外和 CPU 内两部分</p><ol><li>CPU 外：外部设备的中断由中断代理芯片接收，处理后将该中断的中断向量号发送到 CPU</li><li>CPU 内：CPU 执行该中断向量号对应的中断处理程序</li></ol><p>CPU 外这部分的处理过程，在后面咱们讲述<strong>中断代理芯片 Intel 8259A</strong> 时大家就会了解，这部分内容属于<strong>硬件范畴</strong></p><p>/….略</p><hr><h3 id="E-5-可编程中断控制器-8259A"><a href="#E-5-可编程中断控制器-8259A" class="headerlink" title="E.5 可编程中断控制器 8259A"></a>E.5 可编程中断控制器 8259A</h3><p>本节将介绍可屏蔽中断的代理—可编程中断控制器 8259A</p><p>8259A 用于管理和控制可屏蔽中断，它表现在屏蔽外设中断，对它们实行优先级判决，向 CPU 提供中断向量号等功能。而它称为可编程的原因，就是可以通过编程的方式来设置以上的功能</p><blockquote><p>Intel 处理器共支持 256 个中断，但 8259A 只可以管理 8 个中断，不知道 Intel 这是要闹哪样，所以它为了多支持一些中断设备，提供了另一个解决方案，将多个 8259A 组合，官方术语就是级联。有了级联这种组合后，每一个 8259A 就被称为 1 片。若采用级联方式，即多片 8259A 芯片串连在一起，最多可级联 9 个，也就是最多支持 64 个中断。n 片 8259A 通过级联可支持 7n+1 个中断源，级联时只能有一片 8259A为主片 master，其余的均为从片 slave。来自从片的中断只能传递给主片，再由主片向上传递给 CPU，也就是说只有主片才会向 CPU 发送 INT 中断信号</p></blockquote><p>我们主要要干两件事，也就是构建中断处理框架的流程<br>(1) 构造好IDT(中断描述符表)<br>(2) 提供中断向量号</p><p>还是实例才能更好的记住,下面来介绍如何编程:</p><p>对它的编程就是<strong>对它进行初始化，设置主片与从片的级联方式，指定起始中断向量号以及设置各种工作模式</strong></p><blockquote><p>其实，在开机之后的实模式下，BIOS 也对它光顾过，8259A 的 IRQ0～7已经被 BIOS 分配了 0x8～0xf 的中断向量号。而在保护模式下，中断向量号已经为 0x8～0xf 的范围已经被 CPU 占了，分配给了各种异常，咱们还得重新为 8259A 芯片上的 IRQ 接口们分配中断向量号<br>中断向量号是逻辑上的东西，它在物理上是 8259A 上的 IRQ 接口号。8259A 上 IRQ 号的排列顺序是固定的，但其对应的中断向量号是不固定的，这其实是一种由硬件到软件的映射，通过设置 8259A，可以将 IRQ 接口映射到不同的中断向量号</p></blockquote><p>其中具体的控制方法请大家移动到书中看，书中介绍的非常详细</p><h3 id="E-6-编写中断处理程序"><a href="#E-6-编写中断处理程序" class="headerlink" title="E.6 编写中断处理程序"></a>E.6 编写中断处理程序</h3><p>这里实现第一个中断处理程序，写一个简陋的时钟中断，再逐渐丰富它</p><blockquote><p>Intel 8259A 芯片位于主板上的南桥芯片中，咱们不需要像网卡、硬盘那样单独安装才能用，也不需要为它的各个 IR 引脚指定连接的外部设备，这一切都安排好啦，比如主片 IR0 引脚上就是时钟中断，这已经由内部电路实现了，咱们只需要直接操作 8259A 就行，不用担心这些外部设备是否连接上了 8259A</p></blockquote><p>下面是这次的中断启动流程框架:</p><p>int_all(用来初始化所有的设备及数据结构) –&gt; idt_init(它用来初始化中断相关的内容) –&gt;pic_init(初始化可编程中断控制器 8259A) &amp;&amp; idt_desc_init(初始化中断描述符表 IDT) –&gt; 加载IDT(现在便准备好了打开中断的条件)</p><p>下面来开始写代码吧(文件路径为:/home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel.S)，这里面还使用了新的内容：宏，具体内容在代码中我会打出注释<br>这里我直接给出所有编写的文件，大家可以参考书中内容来阅读，我写了一小部分在注释中</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** /home/mouse/OS_mouse/tool/bochs/mouse/kernel/main.c *****/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"I am kernel!\n"</span>);</span><br><span class="line">    init_all();                 <span class="comment">//初始化所有中断</span></span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"sti"</span>)</span> ;       <span class="comment">//暂时打开中断</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);                   <span class="comment">//防止退出</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/init.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化所有模块</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_all</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"init_all\n"</span>);</span><br><span class="line">    idt_init();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/init.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __INIT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __INIT_H</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_all</span><span class="params">()</span>;        <span class="comment">//初始化所有模块</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span>              <span class="comment">//put_str</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span>                 <span class="comment">//io操作，联合汇编</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_CNT 0x21       <span class="comment">// 目前总共支持的中断数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_CTRL 0x20         <span class="comment">// 主片的控制端口是 0x20 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_DATA 0x21         <span class="comment">// 主片的数据端口是 0x21 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_CTRL 0xa0         <span class="comment">// 从片的控制端口是 0xa0 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_DATA 0xa1         <span class="comment">// 从片的数据端口是 0xa1</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 中断门描述符结构体</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span>  <span class="title">gate_desc</span>{</span></span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_low_word;</span><br><span class="line">    <span class="type">uint16_t</span>    selector;</span><br><span class="line">    <span class="type">uint8_t</span>     dcount;     <span class="comment">//此项为双字计数字段，是门描述符中的第4字节。为固定值，不用考虑</span></span><br><span class="line">    <span class="type">uint8_t</span>     attribute;</span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_high_word;     </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态函数声明,非必须</span></span><br><span class="line"><span class="comment">// intr_handler 是自己定义的(interrupt.h) typedef void* intr_handler；</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span>;</span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">gate_desc</span> <span class="title">idt</span>[<span class="title">IDT_DESC_CNT</span>];</span>              <span class="comment">// idt是中断描述符，本质上一个中断门描述符数组</span></span><br><span class="line"><span class="keyword">extern</span> intr_handler intr_entry_table[IDT_DESC_CNT];     <span class="comment">// 声明引用定义kernel.S中的中断处理函数入口函数 </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化可编程中断控制器 8259A */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">pic_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/*初始化主片 */</span> </span><br><span class="line">    outb (PIC_M_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x20</span>); <span class="comment">// ICW2: 起始中断向量号为 0x20 </span></span><br><span class="line">    <span class="comment">//也就是 IR[0-7] 为 0x20 ～ 0x27 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x04</span>); <span class="comment">// ICW3: IR2 接从片</span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line">    <span class="comment">/*初始化从片 */</span> </span><br><span class="line">    outb (PIC_S_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x28</span>); <span class="comment">// ICW2: 起始中断向量号为 0x28 </span></span><br><span class="line">    <span class="comment">// 也就是 IR[8-15]为 0x28 ～ 0x2F </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x02</span>); <span class="comment">// ICW3: 设置从片连接到主片的 IR2 引脚</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/*打开主片上 IR0,也就是目前只接受时钟产生的中断 */</span> </span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0xfe</span>); </span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0xff</span>); </span><br><span class="line">    put_str(<span class="string">" pic_init done\n"</span>); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*创建中断门描述符*/</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 参数: 中断门描述符的指针，中断描述符内的属性，中断描述符内对应的中断处理函数</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span></span><br><span class="line">{</span><br><span class="line">    p_gdesc-&gt;func_offset_low_word = (<span class="type">uint32_t</span>)function &amp; <span class="number">0x0000FFFF</span>;</span><br><span class="line">    p_gdesc-&gt;selector = SELECTOR_K_CODE;        <span class="comment">//定义在global.h : 指向内核数据段的选择子</span></span><br><span class="line">    p_gdesc-&gt;dcount = <span class="number">0</span>;</span><br><span class="line">    p_gdesc-&gt;attribute = attr;</span><br><span class="line">    p_gdesc-&gt;func_offset_high_word = ((<span class="type">uint32_t</span>)function &amp; <span class="number">0Xffff0000</span>) &gt;&gt;<span class="number">16</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*初始化中断描述符*/</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">idt_desc_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        make_idt_desc(&amp;idt[i],IDT_DESC_ATTR_DPL0,intr_entry_table[i]);</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"idt_desc_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*完成有关中断的所有初始化工作*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"idt_init start\n"</span>);</span><br><span class="line">    idt_desc_init();                <span class="comment">//初始化中断描述符表</span></span><br><span class="line">    pic_init();                     <span class="comment">//初始化8259A</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载idt</span></span><br><span class="line">    <span class="type">uint64_t</span> idt_operand = ((<span class="keyword">sizeof</span>(idt) - <span class="number">1</span>) | ((<span class="type">uint64_t</span>)((<span class="type">uint32_t</span>)idt &lt;&lt; <span class="number">16</span>)));</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"lidt %0"</span> ::<span class="string">""</span>(idt_operand))</span>;</span><br><span class="line">    put_str(<span class="string">"idt_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __INTERRUPUT_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __INTERRUPUT_H</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span>* intr_handler;</span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/global.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __KERNEL_GLOBAL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __KERNEL_GLOBAL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL0 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL1 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL2 2</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RPL3 3</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TI_GDT 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TI_LDT 1</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_CODE ((1 &lt;&lt; 3 ) + (TI_GDT &lt;&lt; 2) + RPL0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_DATA ((2 &lt;&lt; 3 ) + (TI_GDT &lt;&lt; 2) + RPL0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_STACK    SELECTOR_K_DATA</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SELECTOR_K_GS   ((3 &lt;&lt; 3 ) + (TI_GDT &lt;&lt; 2) + RPL0)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*------------- IDT 描述符属性 ----------------------*/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_P 1 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_DPL0 0 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_DPL3 3 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_32_TYPE 0xE        <span class="comment">// 32 位的门</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_16_TYPE 0x6        <span class="comment">// 16 位的门,不会用到</span></span></span><br><span class="line">                                    <span class="comment">// 定义它只为和 32 位门区分</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_ATTR_DPL0      ((IDT_DESC_P &lt;&lt; 7) + (IDT_DESC_DPL0 &lt;&lt; 5) + IDT_DESC_32_TYPE) </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_ATTR_DPL3      ((IDT_DESC_P &lt;&lt; 7) + (IDT_DESC_DPL3 &lt;&lt; 5) + IDT_DESC_32_TYPE) </span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel.S</span></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">%define</span> ERROR_CODE <span class="keyword">nop</span>      <span class="comment">;若在相关的异常中 CPU 已经自动压入了</span></span><br><span class="line">                            <span class="comment">;错误码，为保持栈中格式统一，这里不做操作</span></span><br><span class="line"><span class="meta">%define</span> <span class="meta">ZERO</span> <span class="keyword">push</span> <span class="number">0</span>         <span class="comment">;若在相关的异常中 CPU 没有压入错误码</span></span><br><span class="line">                            <span class="comment">;为了统一栈中格式，就手工压入一个 0</span></span><br><span class="line"></span><br><span class="line"><span class="meta">extern</span> put_str              <span class="comment">;声明外部函数</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> .data</span><br><span class="line">intr_str <span class="built_in">db</span> <span class="string">"interrupt occur!"</span>, <span class="number">0xa</span>, <span class="number">0</span>      <span class="comment">;定义了一个以空字符（NULL）结尾的字符串常量</span></span><br><span class="line"><span class="meta">global</span> intr_entry_table                     <span class="comment">;声明可以外部调用</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;下面是宏的使用格式：</span></span><br><span class="line"><span class="comment">; %macro 宏名字 参数个数 ;然后这里用%1，%2...来代替变量</span></span><br><span class="line"><span class="comment">;… </span></span><br><span class="line"><span class="comment">;宏代码体</span></span><br><span class="line"><span class="comment">;… </span></span><br><span class="line"><span class="comment">;%endmacro</span></span><br><span class="line"><span class="comment">;</span></span><br><span class="line"><span class="comment">;</span></span><br><span class="line"><span class="symbol">intr_entry_table:</span></span><br><span class="line">%macro VECTOR <span class="number">2</span>         <span class="comment">;定义VECTOR的宏，两个参数，宏开始</span></span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line">intr<span class="subst">%1</span>entry::            <span class="comment">;每个中断处理程序都要压入中断向量号--中断入口标签</span></span><br><span class="line">                        <span class="comment">;所以一个中断类型一个中断处理函数</span></span><br><span class="line">                        <span class="comment">;自己知道自己的中断向量号是多少</span></span><br><span class="line">    <span class="subst">%2</span>                  <span class="comment">;第二个参数,ZERO,统一栈格式</span></span><br><span class="line">    <span class="keyword">push</span> intr_str       <span class="comment">;压入字符串</span></span><br><span class="line">    <span class="keyword">call</span> put_str        <span class="comment">;调用打印函数</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">4</span>           <span class="comment">;跳过参数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;如果是从片上进入的中断，除了往从片上发送 EOI 外，还要往主片上发送 EOI</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>,<span class="number">0x20</span>         <span class="comment">;中断结束命令 EOI</span></span><br><span class="line">    <span class="keyword">out</span> <span class="number">0xa0</span>,<span class="built_in">al</span>         <span class="comment">;向从片发送</span></span><br><span class="line">    <span class="keyword">out</span> <span class="number">0x20</span>,<span class="built_in">al</span>         <span class="comment">;向主片发送</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">4</span>           <span class="comment">;跳过error_code</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">iret</span>                <span class="comment">;从中断返回，32 位下等同指令 iretd</span></span><br><span class="line">    </span><br><span class="line"><span class="meta">section</span> .data</span><br><span class="line">    <span class="built_in">dd</span> intr<span class="subst">%1</span>entry:      <span class="comment">;存储各个中断入口程序的地址</span></span><br><span class="line">                        <span class="comment">;形成 intr_entry_table</span></span><br><span class="line">%endmacro               <span class="comment">;宏结束</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;这里将所有中断都指向打印字符串</span></span><br><span class="line"><span class="comment">;第 1 个参数 0x1e 是中断向量号,第二个参数主要是用来占位，表示是否自动压入错误码(ERROR_CODE)</span></span><br><span class="line">VECTOR <span class="number">0x00</span>,<span class="meta">ZERO</span>    <span class="comment">; 除法错误</span></span><br><span class="line">VECTOR <span class="number">0x01</span>,<span class="meta">ZERO</span>    <span class="comment">; 调试异常</span></span><br><span class="line">VECTOR <span class="number">0x02</span>,<span class="meta">ZERO</span>    <span class="comment">; 非屏蔽中断 (NMI)</span></span><br><span class="line">VECTOR <span class="number">0x03</span>,<span class="meta">ZERO</span>    <span class="comment">; 断点 (INT3)</span></span><br><span class="line">VECTOR <span class="number">0x04</span>,<span class="meta">ZERO</span>    <span class="comment">; 溢出 (INTO)</span></span><br><span class="line">VECTOR <span class="number">0x05</span>,<span class="meta">ZERO</span>    <span class="comment">; 边界范围超出 (BOUND)</span></span><br><span class="line">VECTOR <span class="number">0x06</span>,<span class="meta">ZERO</span>    <span class="comment">; 无效操作码 (UD2)</span></span><br><span class="line">VECTOR <span class="number">0x07</span>,<span class="meta">ZERO</span>    <span class="comment">; 设备不可用</span></span><br><span class="line">VECTOR <span class="number">0x08</span>,ERROR_CODE <span class="comment">; 双重错误</span></span><br><span class="line">VECTOR <span class="number">0x09</span>,<span class="meta">ZERO</span>    <span class="comment">; 协处理器段超限</span></span><br><span class="line">VECTOR <span class="number">0x0a</span>,ERROR_CODE <span class="comment">; 无效TSS</span></span><br><span class="line">VECTOR <span class="number">0x0b</span>,ERROR_CODE <span class="comment">; 段不存在</span></span><br><span class="line">VECTOR <span class="number">0x0c</span>,<span class="meta">ZERO</span>    <span class="comment">; 栈段错误</span></span><br><span class="line">VECTOR <span class="number">0x0d</span>,ERROR_CODE <span class="comment">; 通用保护错误</span></span><br><span class="line">VECTOR <span class="number">0x0e</span>,ERROR_CODE <span class="comment">; 页错误</span></span><br><span class="line">VECTOR <span class="number">0x0f</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x10</span>,<span class="meta">ZERO</span>    <span class="comment">; x87浮点异常</span></span><br><span class="line">VECTOR <span class="number">0x11</span>,ERROR_CODE <span class="comment">; 对齐检查</span></span><br><span class="line">VECTOR <span class="number">0x12</span>,<span class="meta">ZERO</span>    <span class="comment">; 机器检查</span></span><br><span class="line">VECTOR <span class="number">0x13</span>,<span class="meta">ZERO</span>    <span class="comment">; SIMD浮点异常</span></span><br><span class="line">VECTOR <span class="number">0x14</span>,<span class="meta">ZERO</span>    <span class="comment">; 虚拟化异常</span></span><br><span class="line">VECTOR <span class="number">0x15</span>,<span class="meta">ZERO</span>    <span class="comment">; 控制保护异常</span></span><br><span class="line">VECTOR <span class="number">0x16</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x17</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x18</span>,ERROR_CODE <span class="comment">; 超线程技术异常</span></span><br><span class="line">VECTOR <span class="number">0x19</span>,<span class="meta">ZERO</span>    <span class="comment">; VMM通信异常</span></span><br><span class="line">VECTOR <span class="number">0x1a</span>,ERROR_CODE <span class="comment">; 安全异常</span></span><br><span class="line">VECTOR <span class="number">0x1b</span>,ERROR_CODE <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x1c</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x1d</span>,ERROR_CODE <span class="comment">; 保护模式异常</span></span><br><span class="line">VECTOR <span class="number">0x1e</span>,ERROR_CODE <span class="comment">; 安全启动异常</span></span><br><span class="line">VECTOR <span class="number">0x1f</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x20</span>,<span class="meta">ZERO</span>    <span class="comment">; 通常用于系统调用或定时器中断</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/******************机器模式 ******************* </span></span><br><span class="line"><span class="comment">b -- 输出寄存器 QImode 名称，即寄存器中的最低 8 位:[a-d]l </span></span><br><span class="line"><span class="comment">w -- 输出寄存器 HImode 名称，即寄存器中 2 个字节的部分,如[a-d]x </span></span><br><span class="line"><span class="comment">HImode </span></span><br><span class="line"><span class="comment">"Half-Integer"模式，表示一个两字节的整数</span></span><br><span class="line"><span class="comment">QImode </span></span><br><span class="line"><span class="comment">"Quarter-Integer"模式，表示一个一字节的整数</span></span><br><span class="line"><span class="comment">******************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __LIB_IO_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __LIB_IO_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*这里使用static(静态) inline(内嵌)的意思就是为了让执行更快*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 向端口port 写入一个字节*/</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">outb</span><span class="params">(<span class="type">uint16_t</span> port ,<span class="type">uint8_t</span> data)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">/*对端口指定 N 表示 0～255, d 表示用 dx 存储端口号, %b0 表示对应 al,%w1 表示对应 dx */</span></span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"outb %b0  ,%w1"</span> :: <span class="string">"a"</span> (data),<span class="string">"Nd"</span> (port))</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将addr 处其实的word_cnt个字写入端口port*/</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">outsw</span><span class="params">(<span class="type">uint16_t</span> port, <span class="type">const</span> <span class="type">void</span>* addr, <span class="type">uint32_t</span> word_cnt)</span></span><br><span class="line">{</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/*  +表示此限制即做输入,又做输出 </span></span><br><span class="line"><span class="comment">        ooutsw 是把 ds:esi 处的 16 位的内容写入 port 端口，我们在设置段描述符时</span></span><br><span class="line"><span class="comment">        已经将 ds,es,ss 段的选择子都设置为相同的值了，此时不用担心数据错乱  */</span></span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"cld; rep outsw"</span> : <span class="string">"+S"</span> (addr), <span class="string">"+c"</span> (word_cnt) : <span class="string">"d"</span> (port))</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将从端口 port 读入的一个字节返回 */</span> </span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">uint8_t</span> <span class="title function_">inb</span><span class="params">(<span class="type">uint16_t</span> port)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">uint8_t</span> data; </span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"inb %w1, %b0"</span> : <span class="string">"=a"</span> (data) : <span class="string">"Nd"</span> (port))</span>; </span><br><span class="line">    <span class="keyword">return</span> data; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将从端口 port 读入的 word_cnt 个字写入 addr */</span> </span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">insw</span><span class="params">(<span class="type">uint16_t</span> port, <span class="type">void</span>* addr, <span class="type">uint32_t</span> word_cnt)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"cld; rep insw"</span> : <span class="string">"+D"</span> (addr), <span class="string">"+c"</span> (word_cnt) : <span class="string">"d"</span> (port) : <span class="string">"memory"</span>)</span>; </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>大家可以通过main函数入手，一步一步看函数引用，这里大体介绍一下:</p><p><code>/mouse/kernel:</code><br><code>kernel.S</code>文件里面添加了中断处理程序<br><code>global.h</code>文件里面添加了常用的宏(IDT描述符相关的的宏)<br><code>interrupt.c</code>文件里面添加了：初始化中断控制器8259A/中断描述符等<br><code>interrupt.h</code>文件里面原始中断初始化函数的定义<br><code>init.c</code>文件里面定义了主函数调用的初始化函数<br><code>init.h</code>文件里面添加定义</p><p><code>/mouse/lib/kernel</code><br><code>io.h</code>文件里面添加了对IO口的操作，通过汇编实现(为了快速调用展开)</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">make        <span class="comment">#编译</span></span><br><span class="line">make img    <span class="comment">#写入磁盘</span></span><br></pre></td></tr></table></figure></div><p>最后通过前面写的Makefile文件来运行，会发现屏幕上移一直打印`nterrupt occur!``<br>这是因为我们开启了时钟产生的中断，然后进入kernel.S里面的汇编函数，打印对应内容<br>当然这一节主要是学习中断是如何构建的，<strong>如何编程中断控制器8259A，大家可以仔细阅读书中的解释，那里写的十分详细</strong></p><hr><h3 id="E-7-改进中断处理程序"><a href="#E-7-改进中断处理程序" class="headerlink" title="E.7 改进中断处理程序"></a>E.7 改进中断处理程序</h3><p>现在可以看到我们的中断处理函数是在kernel.S文件中，并且使用汇编语言，这样子的函数肯定不适合我们后期使用修改，所以以后我们用C语言书写中断处理函数，然后再汇编中调用即可<br>方案如下:<strong>在汇编版本的 intrXXentry 中调用 C 语言版本的中断处理函数。这样汇编中的 intrXXentry 就如其名一样，真正变成了中断的 entry，即入口,然后在C语言中建立一个中断处理数组idt_table,数组元素是C语言中断处理函数的地址，供汇编代码中的intrXXentry调用</strong></p><p>然后这里要考虑在汇编中要如何方便的调用C语言的中断处理函数的地址<br>因为我们是模拟32位的操作系统，所以C语言里面的数组也是32位的，也就是4个字节，那么相对数组首地址(idt_table)的偏移就是4*n(第n个元素)，比如数组中的第二个地址就可以表示为<code>idt_table+1*4</code>，注意这里的第二个的n是1，而中断向量号也是从0开始的，所以就可以表示为<code>idt_table+中断向量号*4</code></p><p>下面主要修改两个文件<code>interrupt.c</code> 和 <code>kernel.S</code>,为了方便查看/复制，这里还是选择给出完整代码<br>同时，如果大家使用的是vscode可以选择添加一个<code>.vscode</code>文件夹，然后写一个配置文件<code>c_cpp_properties.json</code>，这样就可以包含其它头文件，方便代码编写：</p><div class="code-container" data-rel="Json"><figure class="iseeu highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"configurations"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">{</span></span><br><span class="line">            <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Linux"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"includePath"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="string">"${workspaceFolder}/**"</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse/lib/kernel"</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse/lib/user"</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse/lib"</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">"/home/mouse/OS_mouse/tool/bochs/mouse/kernel"</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"defines"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"compilerPath"</span><span class="punctuation">:</span> <span class="string">"/usr/bin/gcc"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"cStandard"</span><span class="punctuation">:</span> <span class="string">"c11"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"cppStandard"</span><span class="punctuation">:</span> <span class="string">"c++17"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"intelliSenseMode"</span><span class="punctuation">:</span> <span class="string">"linux-gcc-x64"</span> </span><br><span class="line">        <span class="punctuation">}</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"version"</span><span class="punctuation">:</span> <span class="number">4</span> <span class="comment">//</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure></div><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>interrupt.c： 点击查看更多 </summary>              <div class="content">              <div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/interrupt.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span>              <span class="comment">//put_str</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span>                 <span class="comment">//io操作，联合汇编</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDT_DESC_CNT 0x33       <span class="comment">// 目前总共支持的中断数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_CTRL 0x20         <span class="comment">// 主片的控制端口是 0x20 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_M_DATA 0x21         <span class="comment">// 主片的数据端口是 0x21 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_CTRL 0xa0         <span class="comment">// 从片的控制端口是 0xa0 </span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIC_S_DATA 0xa1         <span class="comment">// 从片的数据端口是 0xa1</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 中断门描述符结构体</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span>  <span class="title">gate_desc</span>{</span></span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_low_word;</span><br><span class="line">    <span class="type">uint16_t</span>    selector;</span><br><span class="line">    <span class="type">uint8_t</span>     dcount;     <span class="comment">//此项为双字计数字段，是门描述符中的第4字节。为固定值，不用考虑</span></span><br><span class="line">    <span class="type">uint8_t</span>     attribute;</span><br><span class="line">    <span class="type">uint16_t</span>    func_offset_high_word;     </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态函数声明,非必须</span></span><br><span class="line"><span class="comment">// intr_handler 是自己定义的(interrupt.h) typedef void* intr_handler；</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span>;</span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">gate_desc</span> <span class="title">idt</span>[<span class="title">IDT_DESC_CNT</span>];</span>              <span class="comment">// idt是中断描述符，本质上一个中断门描述符数组</span></span><br><span class="line"><span class="keyword">extern</span> intr_handler intr_entry_table[IDT_DESC_CNT];     <span class="comment">// 声明引用定义kernel.S中的中断处理函数入口函数 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">//添加部分:</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exception_init</span><span class="params">(<span class="type">void</span>)</span>;                   <span class="comment">//完成一般中断处理函数注册及异常名称注册</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">general_intr_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span>;   <span class="comment">//通用的中断处理函数，一般在异常出现时处理</span></span><br><span class="line"><span class="type">const</span> <span class="type">char</span>* intr_name[IDT_DESC_CNT];                <span class="comment">//用于保留异常的名字</span></span><br><span class="line">intr_handler idt_table[IDT_DESC_CNT];               <span class="comment">//中断处理函数程序数组,在kernel.S 中定义的 intrXXentry是中断处理函数的入口，最终调用 idt_table 里面的函数</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化可编程中断控制器 8259A */</span> </span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">pic_init</span><span class="params">(<span class="type">void</span>)</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="comment">/*初始化主片 */</span> </span><br><span class="line">    outb (PIC_M_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x20</span>); <span class="comment">// ICW2: 起始中断向量号为 0x20 </span></span><br><span class="line">    <span class="comment">//也就是 IR[0-7] 为 0x20 ～ 0x27 </span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x04</span>); <span class="comment">// ICW3: IR2 接从片</span></span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line">    <span class="comment">/*初始化从片 */</span> </span><br><span class="line">    outb (PIC_S_CTRL, <span class="number">0x11</span>); <span class="comment">// ICW1: 边沿触发,级联 8259, 需要 ICW4 </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x28</span>); <span class="comment">// ICW2: 起始中断向量号为 0x28 </span></span><br><span class="line">    <span class="comment">// 也就是 IR[8-15]为 0x28 ～ 0x2F </span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x02</span>); <span class="comment">// ICW3: 设置从片连接到主片的 IR2 引脚</span></span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0x01</span>); <span class="comment">// ICW4: 8086 模式, 正常 EOI </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/*打开主片上 IR0,也就是目前只接受时钟产生的中断 */</span> </span><br><span class="line">    outb (PIC_M_DATA, <span class="number">0xfe</span>); </span><br><span class="line">    outb (PIC_S_DATA, <span class="number">0xff</span>); </span><br><span class="line">    put_str(<span class="string">" pic_init done\n"</span>); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*创建中断门描述符*/</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 参数: 中断门描述符的指针，中断描述符内的属性，中断描述符内对应的中断处理函数</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">make_idt_desc</span><span class="params">(<span class="keyword">struct</span> gate_desc* p_gdesc,<span class="type">uint8_t</span> attr, intr_handler function)</span></span><br><span class="line">{</span><br><span class="line">    p_gdesc-&gt;func_offset_low_word = (<span class="type">uint32_t</span>)function &amp; <span class="number">0x0000FFFF</span>;</span><br><span class="line">    p_gdesc-&gt;selector = SELECTOR_K_CODE;        <span class="comment">//定义在global.h : 指向内核数据段的选择子</span></span><br><span class="line">    p_gdesc-&gt;dcount = <span class="number">0</span>;</span><br><span class="line">    p_gdesc-&gt;attribute = attr;</span><br><span class="line">    p_gdesc-&gt;func_offset_high_word = ((<span class="type">uint32_t</span>)function &amp; <span class="number">0Xffff0000</span>) &gt;&gt;<span class="number">16</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*初始化中断描述符*/</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">idt_desc_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        make_idt_desc(&amp;idt[i],IDT_DESC_ATTR_DPL0,intr_entry_table[i]);</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"idt_desc_init done\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//添加部分</span></span><br><span class="line"><span class="comment">//通用的中断处理函数，一般在异常出现时处理</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">general_intr_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(vec_nr == <span class="number">0x27</span> || vec_nr == <span class="number">0x2f</span>)    <span class="comment">//IRQ7s和IRQ15会产生伪中断，无需处理，0x2f是从片8259A上的最后一个IRQ引脚，保留项</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"int vector : 0x"</span>);</span><br><span class="line">    put_int(vec_nr);</span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//页中断，我当时调试使用的，大家可以不用管</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">page_fault_handler</span><span class="params">(<span class="type">uint8_t</span> vec_nr)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(vec_nr == <span class="number">0x27</span> || vec_nr == <span class="number">0x2f</span>)    <span class="comment">//IRQ7s和IRQ15会产生伪中断，无需处理，0x2f是从片8259A上的最后一个IRQ引脚，保留项</span></span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    }</span><br><span class="line">    put_str(<span class="string">"int vector : 0x"</span>);</span><br><span class="line">    put_int(vec_nr);</span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">uint32_t</span> faulting_address;</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"mov %%cr2, %0"</span> : <span class="string">"=r"</span> (faulting_address))</span>; <span class="comment">// 读取CR2</span></span><br><span class="line">    put_str(<span class="string">"Page fault at address: 0x"</span>);</span><br><span class="line">    put_int(faulting_address);  <span class="comment">// 十六进制打印</span></span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//完成一般中断处理函数注册及异常名称注册</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exception_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;IDT_DESC_CNT;i++)</span><br><span class="line">    {</span><br><span class="line">        <span class="comment">/* idt_table 数组中的函数是在进入中断后根据中断向量号调用的 */</span></span><br><span class="line">        <span class="comment">/* 见 kernel/kernel.S 的 call [idt_table + %1*4] */</span> </span><br><span class="line">        idt_table[i] = general_intr_handler;    <span class="comment">// 默认为 general_intr_handler ,以后会由 register_handler 来注册具体处理函数</span></span><br><span class="line">        intr_name[i] = <span class="string">"unknown"</span>;               <span class="comment">// 先统一赋值为 unknow</span></span><br><span class="line">    }</span><br><span class="line">    idt_table[<span class="number">14</span>] = page_fault_handler;         <span class="comment">//页错误处理函数</span></span><br><span class="line">    intr_name[<span class="number">0</span>] = <span class="string">"#DE Divide Error"</span>; </span><br><span class="line">    intr_name[<span class="number">1</span>] = <span class="string">"#DB Debug Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">2</span>] = <span class="string">"NMI Interrupt"</span>; </span><br><span class="line">    intr_name[<span class="number">3</span>] = <span class="string">"#BP Breakpoint Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">4</span>] = <span class="string">"#OF Overflow Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">5</span>] = <span class="string">"#BR BOUND Range Exceeded Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">6</span>] = <span class="string">"#UD Invalid Opcode Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">7</span>] = <span class="string">"#NM Device Not Available Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">8</span>] = <span class="string">"#DF Double Fault Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">9</span>] = <span class="string">"Coprocessor Segment Overrun"</span>; </span><br><span class="line">    intr_name[<span class="number">10</span>] = <span class="string">"#TS Invalid TSS Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">11</span>] = <span class="string">"#NP Segment Not Present"</span>;</span><br><span class="line">    intr_name[<span class="number">12</span>] = <span class="string">"#SS Stack Fault Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">13</span>] = <span class="string">"#GP General Protection Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">14</span>] = <span class="string">"#PF Page-Fault Exception"</span>; </span><br><span class="line">    <span class="comment">// intr_name[15] 第 15 项是 intel 保留项,未使用</span></span><br><span class="line">    intr_name[<span class="number">16</span>] = <span class="string">"#MF x87 FPU Floating-Point Error"</span>; </span><br><span class="line">    intr_name[<span class="number">17</span>] = <span class="string">"#AC Alignment Check Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">18</span>] = <span class="string">"#MC Machine-Check Exception"</span>; </span><br><span class="line">    intr_name[<span class="number">19</span>] = <span class="string">"#XF SIMD Floating-Point Exception"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*完成有关中断的所有初始化工作*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">idt_init</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"idt_init start\n"</span>);</span><br><span class="line">    idt_desc_init();                <span class="comment">//初始化中断描述符表</span></span><br><span class="line">    exception_init();               <span class="comment">//初始化异常名并注册通常的中断处理函数</span></span><br><span class="line">    pic_init();                     <span class="comment">//初始化8259A</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载idt</span></span><br><span class="line">    <span class="type">uint64_t</span> idt_operand = ((<span class="keyword">sizeof</span>(idt) - <span class="number">1</span>) | ((<span class="type">uint64_t</span>)((<span class="type">uint32_t</span>)idt &lt;&lt; <span class="number">16</span>)));</span><br><span class="line">    <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"lidt %0"</span> ::<span class="string">""</span>(idt_operand))</span>;</span><br><span class="line">    put_str(<span class="string">"idt_init done\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>              </div>            </details><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>kernel.S： 点击查看更多 </summary>              <div class="content">              <div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/kernel/kernel.S</span></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>]</span><br><span class="line"><span class="meta">%define</span> ERROR_CODE <span class="keyword">nop</span>      <span class="comment">;若在相关的异常中 CPU 已经自动压入了</span></span><br><span class="line">                            <span class="comment">;错误码，为保持栈中格式统一，这里不做操作</span></span><br><span class="line"><span class="meta">%define</span> <span class="meta">ZERO</span> <span class="keyword">push</span> <span class="number">0</span>         <span class="comment">;若在相关的异常中 CPU 没有压入错误码</span></span><br><span class="line">                            <span class="comment">;为了统一栈中格式，就手工压入一个 0</span></span><br><span class="line"></span><br><span class="line"><span class="meta">extern</span> put_str              <span class="comment">;声明外部函数</span></span><br><span class="line"><span class="meta">extern</span> idt_table            <span class="comment">;声明c中注册的中断处理程序数组</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> .data</span><br><span class="line"><span class="meta">global</span> intr_entry_table                     <span class="comment">;声明可以外部调用</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;下面是宏的使用格式：</span></span><br><span class="line"><span class="comment">; %macro 宏名字 参数个数 ;然后这里用%1，%2...来代替变量</span></span><br><span class="line"><span class="comment">;… </span></span><br><span class="line"><span class="comment">;宏代码体</span></span><br><span class="line"><span class="comment">;… </span></span><br><span class="line"><span class="comment">;%endmacro</span></span><br><span class="line"><span class="comment">;</span></span><br><span class="line"><span class="comment">;</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">intr_entry_table:</span></span><br><span class="line">%macro VECTOR <span class="number">2</span>         <span class="comment">;定义VECTOR的宏，两个参数，宏开始</span></span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line">intr<span class="subst">%1</span>entry:            <span class="comment">;每个中断处理程序都要压入中断向量号--中断入口标签，具体值也就是中断向量号</span></span><br><span class="line">                        <span class="comment">;所以一个中断类型一个中断处理函数</span></span><br><span class="line">                        <span class="comment">;自己知道自己的中断向量号是多少</span></span><br><span class="line">    <span class="subst">%2</span>                  <span class="comment">;第二个参数,ZERO,统一栈格式</span></span><br><span class="line">    <span class="comment">;保存上下文环境</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">ds</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">es</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">fs</span></span><br><span class="line">    <span class="keyword">push</span> <span class="built_in">gs</span></span><br><span class="line">    <span class="keyword">pushad</span>              <span class="comment">;压入32位寄存器，入栈顺序是：EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI，其中EAX 最先入栈</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;如果是从片上进入的中断，除了往从片上发送 EOI 外，还要往主片上发送 EOI</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>,<span class="number">0x20</span>         <span class="comment">;中断结束命令 EOI</span></span><br><span class="line">    <span class="keyword">out</span> <span class="number">0xa0</span>,<span class="built_in">al</span>         <span class="comment">;向从片发送</span></span><br><span class="line">    <span class="keyword">out</span> <span class="number">0x20</span>,<span class="built_in">al</span>         <span class="comment">;向主片发送</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">push</span> <span class="subst">%1</span>             <span class="comment">;不管idt_tableA目标重新是否需要参数，一律压入中断向量号，调试很方便</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">call</span> [idt_table + <span class="subst">%1</span>*<span class="number">4</span>] <span class="comment">;调用C版本的中断处理函数</span></span><br><span class="line">    <span class="keyword">jmp</span> intr_exit           <span class="comment">;调用退出--恢复环境等</span></span><br><span class="line">    </span><br><span class="line"><span class="meta">section</span> .data</span><br><span class="line">    <span class="built_in">dd</span> intr<span class="subst">%1</span>entry      <span class="comment">;存储各个中断入口程序的地址</span></span><br><span class="line">                        <span class="comment">;形成 intr_entry_table</span></span><br><span class="line">%endmacro               <span class="comment">;宏结束</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> .text</span><br><span class="line"><span class="meta">global</span> intr_exit</span><br><span class="line"><span class="symbol">intr_exit:</span></span><br><span class="line">    <span class="comment">;回复上下文环境</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">4</span>       <span class="comment">;跳过中断号</span></span><br><span class="line">    <span class="keyword">popad</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">gs</span> </span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">fs</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">es</span></span><br><span class="line">    <span class="keyword">pop</span> <span class="built_in">ds</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span> ,<span class="number">4</span>      <span class="comment">;跳过error_code</span></span><br><span class="line">    <span class="keyword">iretd</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;这里将中断都指向对应的函数[idt_table + %1*4]</span></span><br><span class="line"><span class="comment">;第 1 个参数 0x1e 是中断向量号,第二个参数主要是用来占位，表示是否自动压入错误码(ERROR_CODE)</span></span><br><span class="line">VECTOR <span class="number">0X00</span>,<span class="meta">ZERO</span>    <span class="comment">; 除法错误异常</span></span><br><span class="line">VECTOR <span class="number">0x01</span>,<span class="meta">ZERO</span>    <span class="comment">; 调试异常</span></span><br><span class="line">VECTOR <span class="number">0x02</span>,<span class="meta">ZERO</span>    <span class="comment">; 非屏蔽中断 (NMI)</span></span><br><span class="line">VECTOR <span class="number">0x03</span>,<span class="meta">ZERO</span>    <span class="comment">; 断点 (INT3)</span></span><br><span class="line">VECTOR <span class="number">0x04</span>,<span class="meta">ZERO</span>    <span class="comment">; 溢出 (INTO)</span></span><br><span class="line">VECTOR <span class="number">0x05</span>,<span class="meta">ZERO</span>    <span class="comment">; 边界范围超出 (BOUND)</span></span><br><span class="line">VECTOR <span class="number">0x06</span>,<span class="meta">ZERO</span>    <span class="comment">; 无效操作码 (UD2)</span></span><br><span class="line">VECTOR <span class="number">0x07</span>,<span class="meta">ZERO</span>    <span class="comment">; 设备不可用</span></span><br><span class="line">VECTOR <span class="number">0x08</span>,ERROR_CODE <span class="comment">; 双重错误</span></span><br><span class="line">VECTOR <span class="number">0x09</span>,<span class="meta">ZERO</span>    <span class="comment">; 协处理器段超限</span></span><br><span class="line">VECTOR <span class="number">0x0a</span>,ERROR_CODE <span class="comment">; 无效TSS</span></span><br><span class="line">VECTOR <span class="number">0x0b</span>,ERROR_CODE <span class="comment">; 段不存在</span></span><br><span class="line">VECTOR <span class="number">0x0c</span>,<span class="meta">ZERO</span>    <span class="comment">; 栈段错误</span></span><br><span class="line">VECTOR <span class="number">0x0d</span>,ERROR_CODE <span class="comment">; 通用保护错误</span></span><br><span class="line">VECTOR <span class="number">0x0e</span>,ERROR_CODE <span class="comment">; 页错误</span></span><br><span class="line">VECTOR <span class="number">0x0f</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x10</span>,<span class="meta">ZERO</span>    <span class="comment">; x87浮点异常</span></span><br><span class="line">VECTOR <span class="number">0x11</span>,ERROR_CODE <span class="comment">; 对齐检查</span></span><br><span class="line">VECTOR <span class="number">0x12</span>,<span class="meta">ZERO</span>    <span class="comment">; 机器检查</span></span><br><span class="line">VECTOR <span class="number">0x13</span>,<span class="meta">ZERO</span>    <span class="comment">; SIMD浮点异常</span></span><br><span class="line">VECTOR <span class="number">0x14</span>,<span class="meta">ZERO</span>    <span class="comment">; 虚拟化异常</span></span><br><span class="line">VECTOR <span class="number">0x15</span>,<span class="meta">ZERO</span>    <span class="comment">; 控制保护异常</span></span><br><span class="line">VECTOR <span class="number">0x16</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x17</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x18</span>,ERROR_CODE <span class="comment">; 超线程技术异常</span></span><br><span class="line">VECTOR <span class="number">0x19</span>,<span class="meta">ZERO</span>    <span class="comment">; VMM通信异常</span></span><br><span class="line">VECTOR <span class="number">0x1a</span>,ERROR_CODE <span class="comment">; 安全异常</span></span><br><span class="line">VECTOR <span class="number">0x1b</span>,ERROR_CODE <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x1c</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x1d</span>,ERROR_CODE <span class="comment">; 保护模式异常</span></span><br><span class="line">VECTOR <span class="number">0x1e</span>,ERROR_CODE <span class="comment">; 安全启动异常</span></span><br><span class="line">VECTOR <span class="number">0x1f</span>,<span class="meta">ZERO</span>    <span class="comment">; 保留</span></span><br><span class="line">VECTOR <span class="number">0x20</span>,<span class="meta">ZERO</span>    <span class="comment">; 通常用于系统调用或定时器中断，这里用来测试中断</span></span><br></pre></td></tr></table></figure></div>              </div>            </details><p>这里我有遇到一个小错误，那就是第一次写的时候在kernel.S文件中没有添加<code>VECTOR 0x00,ZERO</code>,使得描述符表不完整，然后就会一直中断报错(页错误)，也是在这个实验结果下发现的，因为这次修改之后，添加了中断号的显示，正常显示应该是<code>0x20</code>,但是我的显示是<code>0XE</code>，也就是页错误</p><p>后面书中还具体讲了调试，也就是查看这个中断的具体除法流程，包括压栈出栈，大家可以去书中阅读</p><hr><h3 id="E-8253可编程计数器-定时器-简介"><a href="#E-8253可编程计数器-定时器-简介" class="headerlink" title="E.8253可编程计数器/定时器 简介"></a>E.8253可编程计数器/定时器 简介</h3><p>计算机中的时钟，大致上可分为两大类：内部时钟和外部时钟</p><blockquote><p>内部时钟是指处理器中内部元件，如运算器、控制器的工作时序，主要用于控制、同步内部工作过程的步调。内部时钟是由晶体振荡器产生的，简称晶振，它位于主板上，其频率经过分频之后就是主板的外频，处理器和南北桥之间的通信就基于外频,通常是ns级别的，<strong>内部定时是无法改变的</strong></p></blockquote><blockquote><p>外部时钟是指处理器与外部设备或外部设备之间通信时采用的一种时序，比如 IO 接口和处理器之间在 A/D 转换时的工作时序、两个串口设备之间进行数据传输时也要事先同步时钟等。外部设备的速度对于处理器来说就很慢了，所以其时钟的时间单位粒度较大，一般是毫秒（ms）级或秒（s）级的</p></blockquote><p>对于外部定时，我们有两种实现方式:</p><ul><li><strong>软件实现</strong></li></ul><p><code>int cycle_cnt = 90000;</code><br><code>while(cycle_cnt-- &gt; 0);</code></p><p>通过执行空循环达成一定的延时，但这样会浪费处理器的时钟周期</p><ul><li><strong>硬件实现</strong></li></ul><p>这类实现定时的硬件叫做定时器，计时器的功能就是定时发信号。当到达了所计数的时间，计数器可以自动发一个输出信号，可以用该信号向处理器发出中断，和软件定时相比，硬件定时器不占用处理器，因为它是独立运行的，当然还分为可编程或不可编程定时器，下面主要介绍8253:</p><p>硬件定时器一般有两种计时的方式:倒计时、正计时，而8253是倒计时，下面主要说一下怎么让定时器定时，具体的原理比如各种工作模式大家可以移步书中阅读</p><p>（1）GATE 为高电平，即 GATE 为 1，这是由硬件来控制的。<br>（2）计数初值已写入计数器中的减法计数器，这是由软件 out 指令控制的。</p><p>当这两个条件具备后，计数器将在下一个时钟信号 CLK 的下降沿开始计数</p><p>下面来说说8256的初始化步骤:</p><ol><li><strong>往控制字寄存器端口 0x43 中写入控制字</strong></li><li><strong>在所指定使用的计数器端口中写入计数初值</strong> （计数初值寄存器是 16 位，高 8 位和低 8 位可单独使用，所以初值是 8 位或 16 位皆可。若初值是 8 位，直接往计数器端口写入即可。若初值为 16 位，必须分两次来写入，先写低 8 位，再写高 8 位）</li></ol><p>下面来实操一下，让中断引脚的频率加快一点，之前的默认频率为18.206hz，也就是1s 18次，现在我们将它修改成1s发送100次，也就是100HZ</p><p>这个属于设备的代码，我们新建一个文件夹/mouse/device,然后在这里写代码吧,在之前的Makefile部分我已经给出了对应Makefile文件的内容,<strong>注意将里面对timer.c的注释删掉</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/timer.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"timer.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"io.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IRQ0_FREQUENCY 100          <span class="comment">//100HZ</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> INPUT_FREQUENCY 1193180     <span class="comment">//计数器0的工作脉冲信号频率</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY     <span class="comment">//通过宏来计算初值</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONTRER0_PORT 0x40      <span class="comment">//端口2</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER0_NO 0           <span class="comment">//计数器0</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> COUNTER_MODE 2          <span class="comment">//工作模式 方式2</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> READ_WRITE_LATCH 3      <span class="comment">//读写方式，3表示先读写低8位，再读写高8位</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIT_CONTROL_PORT 0x43   <span class="comment">//控制字寄存器的端口</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 把操作的计数器 counter_no､ 读写锁属性 rwl､ 计数器模式 counter_mode 写入模式控制寄存器并赋予初始值 counter_value </span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">frequency_set</span><span class="params">(<span class="type">uint8_t</span> counter_port,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> counter_no,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> rwl,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint8_t</span> counter_mode,\</span></span><br><span class="line"><span class="params">                        <span class="type">uint16_t</span> counter_value)</span> </span><br><span class="line">{ </span><br><span class="line"><span class="comment">//往控制字寄存器端口 0x43 中写入控制字</span></span><br><span class="line">    outb(PIT_CONTROL_PORT, (<span class="type">uint8_t</span>)(counter_no &lt;&lt; <span class="number">6</span> | rwl &lt;&lt; <span class="number">4</span> | counter_mode &lt;&lt; <span class="number">1</span>)); </span><br><span class="line"><span class="comment">//先写入 counter_value 的低 8 位</span></span><br><span class="line">    outb(counter_port, (<span class="type">uint8_t</span>)counter_value); </span><br><span class="line"><span class="comment">//再写入 counter_value 的高 8 位 </span></span><br><span class="line">    outb(counter_port, (<span class="type">uint8_t</span>)counter_value &gt;&gt; <span class="number">8</span>); </span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化 PIT8253</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">timer_init</span><span class="params">()</span> </span><br><span class="line">{ </span><br><span class="line">    put_str(<span class="string">"timer_init start\n"</span>); </span><br><span class="line">    <span class="comment">//设置 8253 的定时周期，也就是发中断的周期</span></span><br><span class="line">    frequency_set(CONTRER0_PORT, \</span><br><span class="line">                    COUNTER0_NO, \</span><br><span class="line">                    READ_WRITE_LATCH,\</span><br><span class="line">                    COUNTER_MODE, \</span><br><span class="line">                    COUNTER0_VALUE); </span><br><span class="line">    put_str(<span class="string">"timer_init done\n"</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/device/timer.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __TIMER_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __TIMER_H</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定时器PIT8253的初始化</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">timer_init</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure></div><p>最后在init.c文件中调用timer_init即可：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// /home/mouse/OS_mouse/tool/bochs/mouse/kernel/init.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"timer.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化所有模块</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_all</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    put_str(<span class="string">"init_all\n"</span>);</span><br><span class="line">    idt_init();             <span class="comment">//中断</span></span><br><span class="line">    timer_init();           <span class="comment">//定时器设备 PIT</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后 <code>make</code> <code>make img</code> 运行即可，但是这里我们好像看不出来定时器中断是否真的加速了咳咳<br>大家可以选择在中断函数中添加一个静态变量，然后每多少次中断更改一次数值，来验证代码是否正确</p><hr><h2 id="F-内存管理前的小结"><a href="#F-内存管理前的小结" class="headerlink" title="F 内存管理前的小结"></a>F 内存管理前的小结</h2><p>这次我们学习了如何内联汇编，实现了基本的打印函数，还有对中断的初始化，最后学习了怎么修改定时器，在这个章节，内联汇编语法显得很重要，同时我们还引入了Makefile文件，比起sh脚本它管理一个大的项目更加有优势，当然，这次的概念也非常多，比如说用户特权，中断描述符等等，还得找个时间好好阅读以下书中的介绍，以及查阅相关资料</p><p><em>这里主要遇到的麻烦可能就是中断初始化那一块的内容，因为涉及大量的操作</em><br>学无止境，下一章就是非常重要的内存管理了~</p><hr><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li>[操作系统真象还原 (郑纲) (Z-Library)]   — 大家可以自己在网上查找相关资源</li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#51;&#49;&#x34;&#54;&#x37;&#48;&#50;&#51;&#54;&#50;&#x40;&#x71;&#113;&#x2e;&#x63;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">这一章主要对《操作系统真相还原》的初入内核以及中断部分来学习，本篇文章主要用来记录学习过程中遇到的问题，以及相关知识概念，方便后来查询......</summary>
    
    
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/categories/OS/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/OS/Linux/"/>
    
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/tags/OS/"/>
    
    <category term="《真相还原》" scheme="https://blog.haozi-haozi.cn/tags/%E3%80%8A%E7%9C%9F%E7%9B%B8%E8%BF%98%E5%8E%9F%E3%80%8B/"/>
    
  </entry>
  
  <entry>
    <title>真象还原 --环境/准备 study(1)</title>
    <link href="https://blog.haozi-haozi.cn/2025/10/21/os_elephant_one/"/>
    <id>https://blog.haozi-haozi.cn/2025/10/21/os_elephant_one/</id>
    <published>2025-10-21T07:45:00.000Z</published>
    <updated>2026-03-24T08:59:29.256Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A 前情提要"></a>A 前情提要</h2><p>在学这个之前，在操作系统方面我可能只了解过 <strong>IMX6U</strong> 的嵌入式linux板子, 以及 <strong>RTOS</strong> 相关的概念，硬件方面主要是 <strong>STM32</strong> ,所以记录的东西可能会相对从基础开始……<br><strong>ps:如果参考本系列文章来实操，需要结合《操作系统真象还原》一起观看，否则会缺失很多细节</strong></p><h2 id="B-前置知识"><a href="#B-前置知识" class="headerlink" title="B 前置知识"></a>B 前置知识</h2><h3 id="B-1-汇编"><a href="#B-1-汇编" class="headerlink" title="B.1 汇编"></a>B.1 汇编</h3><p>首先是去重新复习了一下汇编指令相关的内容，因为在书中讲解编译器，代码段等的时候会有汇编的例子，所以这里简单复习一下基础知识</p><ul><li>指令 = <strong>操作码 + 操作数</strong></li></ul><p>下面主要列出一些常用的内容:</p><blockquote><p><code>mov S,D</code> ：传送，将S的值传递给D</p></blockquote><p>也可以分为 <code>movb</code> <code>movw</code> <code>movl</code> <code>movq</code> 用来传送不同字节大小的内容，分别为1个字节，2个字节，4个字节，8个字节。当然也有条件传递</p><blockquote><p><code>pushl S</code> 把S的值压入栈顶(4个字节)<br><code>popl D</code>  把栈顶的值弹出给D</p></blockquote><p>这里<strong>主要注意操作栈的指令会去修改 esp(栈指针的地址)</strong></p><blockquote><p>计算相关(修改寄存器，设置条件码)<br><code>INC D</code>   D = D+1<br><code>ADD S,D</code> D = S+D<br><code>SUB S,D</code> D = D-S<br><code>OR S,D</code>  D = D|S<br><code>AND S,D</code> D = D&amp;S</p></blockquote><p>这里面比如，ADD指令如果相加溢出之后，也会修改标志位寄存器</p><blockquote><p><code>cmp S2,S1</code> 基于S1-S2设置条件码<br><code>test S2,S1</code> 基于S1&amp;S2设置条件码</p></blockquote><p>这里根据计算结果来设置条件码</p><blockquote><p><code>jmp label</code>   无条件跳转</p></blockquote><p>当然还有别的类型的跳转，这里不详细列出</p><blockquote><p><code>call label</code> 调用函数label，将label放入%eip中<br><code>ret</code> 函数返回，pop %eip</p></blockquote><hr><p>举个例子，通过汇编来实现一个简单的判断x，y差值的绝对值</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">absdiff</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>( x&lt;y&gt; )</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span> y-x;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">return</span> x-y;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>下面是通过汇编来实现–注意这里的<code>x86asm</code>只是为了代码高亮而使用的标识符(后面同理)</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"># 假设 x 在 <span class="number">8</span>(%ebp), y 在 <span class="number">12</span>(%ebp)</span><br><span class="line">    movl <span class="number">8</span>(%ebp), %edx    # <span class="built_in">edx</span> = x</span><br><span class="line">    movl <span class="number">12</span>(%ebp), %eax   # <span class="built_in">eax</span> = y</span><br><span class="line">    cmpl %eax, %edx       # compare x <span class="keyword">and</span> y</span><br><span class="line">    <span class="keyword">jge</span>  .Lge</span><br><span class="line">    subl %edx, %eax       # y = y - x   (覆盖 <span class="built_in">eax</span>，<span class="built_in">eax</span> &lt;- y - x)</span><br><span class="line">    <span class="keyword">jmp</span>  .Ldone</span><br><span class="line"><span class="symbol">.Lge:</span></span><br><span class="line">    subl %eax, %edx       # x = x - y   (覆盖 <span class="built_in">edx</span>，<span class="built_in">edx</span> &lt;- x - y)</span><br><span class="line">    movl %edx, %eax       # 把结果放到 <span class="built_in">eax</span> 以统一返回位置</span><br><span class="line"><span class="symbol">.Ldone:</span></span><br><span class="line">    # 结果在 %eax：为较大者减去较小者（&gt;=<span class="number">0</span>，除非溢出）</span><br></pre></td></tr></table></figure></div><p>CPU对外设的操作通过专门的端口读写指令完成:</p><blockquote><p><code>in al,21H</code> ： 从21H端口调取一字节到al<br><code>out 21H,al</code>: 将al的值写入21H端口</p></blockquote><blockquote><p><code>shr eax, cl</code>: 逻辑右移，将eax右移动cl位</p></blockquote><p>目前就先了解这些，后面的如果实际使用再进行补充学习</p><hr><h2 id="C-书中的操作系统知识—–-一些你可能正感到迷惑的问题"><a href="#C-书中的操作系统知识—–-一些你可能正感到迷惑的问题" class="headerlink" title="C 书中的操作系统知识—–(一些你可能正感到迷惑的问题)"></a>C 书中的操作系统知识—–(一些你可能正感到迷惑的问题)</h2><p>这里也是参考书籍选择性的做一些笔记，了解这些也是为后面实操做准备</p><hr><h3 id="C-1-物理地址、逻辑地址、有效地址、线性地址、虚拟地址的区别"><a href="#C-1-物理地址、逻辑地址、有效地址、线性地址、虚拟地址的区别" class="headerlink" title="C.1 物理地址、逻辑地址、有效地址、线性地址、虚拟地址的区别"></a>C.1 物理地址、逻辑地址、有效地址、线性地址、虚拟地址的区别</h3><ul><li><strong>物理地址</strong> 是物理内存真正的地址，具有唯一性，无论CPU通过虚拟地址，线性地址等访问，最终都是通过物理地址访问，它是访问内存的终点站</li></ul><p>在实模式下，段基址+段内偏移地址，再<strong>经过段部件的处理，然后就会直接输出物理地址</strong>，CPU可以直接访问</p><p><strong>在保护模式下，段基址+段内偏移地址(也称为线性地址)</strong>，需要判断是否打开地址分页的功能，如果没有，那么就可以当做物理地址。如果有，那么需要<strong>通过分页机制来查找对应的物理地址</strong></p><hr><h3 id="C-2-BIOS-中断、DOS-中断、Linux-中断的区别"><a href="#C-2-BIOS-中断、DOS-中断、Linux-中断的区别" class="headerlink" title="C.2 BIOS 中断、DOS 中断、Linux 中断的区别"></a>C.2 BIOS 中断、DOS 中断、Linux 中断的区别</h3><p><strong>BIOS属于固件级的服务例程，DOS属于操作系统级的服务，Linux终端属于操作系统内核的功能</strong>，当然这里是简单了解一下</p><p>BIOS 和 DOS 都是存在于实模式下的程序，由它们建立的中断调用都是建立在中断向量表（Interrupt Vector Table，IVT）中的，它们都是通过软中断指令 int 中断号来调用的</p><p>BIOS是固化在计算机主板上ROM芯片中的一组基础程序。BIOS中断是这组程序提供的服务例程，是计算机加电后最早能使用的软件功能</p><p>DOS是一个16位的单用户单任务操作系统。DOS中断是DOS操作系统提供给应用程序的API（应用程序编程接口）。​DOS本身是构建在BIOS之上的，它的很多功能是通过调用BIOS中断来实现的。</p><hr><h3 id="C-3-实模式，保护模式，长模式分别是什么"><a href="#C-3-实模式，保护模式，长模式分别是什么" class="headerlink" title="C.3 实模式，保护模式，长模式分别是什么"></a>C.3 实模式，保护模式，长模式分别是什么</h3><table><thead><tr><th>特性</th><th>实模式</th><th>保护模式</th><th>长模式</th></tr></thead><tbody><tr><td><strong>出现时间</strong></td><td>8086/8088 (1978)</td><td>80286 (1982)</td><td>AMD Opteron / Athlon 64 (2003)</td></tr><tr><td><strong>地址总线</strong></td><td>20位</td><td>32位（或更多）</td><td>64位</td></tr><tr><td><strong>寻址空间</strong></td><td>1 MB (2^20)</td><td>4 GB (2^32)</td><td>256 TB (理论 2^64，实际 48/57位实现)</td></tr><tr><td><strong>通用寄存器</strong></td><td>16位 (AX, BX, …)</td><td>32位 (EAX, EBX, …)</td><td>64位 (RAX, RBX, …)</td></tr><tr><td><strong>核心特性</strong></td><td>无内存保护，直接物理地址访问</td><td><strong>内存保护</strong>、虚拟内存、多任务硬件支持</td><td><strong>64位扩展</strong>、寄存器数量翻倍、更优的指令集</td></tr><tr><td><strong>寻址方式</strong></td><td><strong>段地址 × 16 + 偏移地址</strong>（物理地址）</td><td><strong>选择子 + 偏移地址</strong>（通过描述符表转换为线性地址）</td><td><strong>基本平坦内存模型</strong>（段基址通常为0）</td></tr><tr><td><strong>特权级</strong></td><td>仅有 Ring 0（所有代码权限相同）</td><td>Ring 0, 1, 2, 3（通常只用 Ring 0-内核 和 Ring 3-用户）</td><td>仅保留 Ring 0 和 Ring 3</td></tr><tr><td><strong>主要应用</strong></td><td>早期DOS系统、BIOS</td><td>所有现代32位操作系统（Windows XP/7, Linux 32位）</td><td>所有现代64位操作系统（Windows 10/11, macOS, Linux 64位）</td></tr></tbody></table><p>ps:后面也有专门讲保护模式的部分</p><hr><p>ps: 如果后面有相关知识补充也是放到这个模块</p><hr><h2 id="D-搭建环境"><a href="#D-搭建环境" class="headerlink" title="D 搭建环境"></a>D 搭建环境</h2><blockquote><p>“C 语言虽然不是为设计大型软件而生的，但其却被用来开发大型软件，现代操作系统基本上是用 C 语言再结合汇编语言开发的，所以 C 语言编译器，我们选择的是 gcc。而汇编语言编译器，我们选择的是 nasm。为什么选择这两个，首先因为它们都是开源软件，其次其强大的功能不亚于同类的商业软件。”</p></blockquote><p>对于这个搭建环境的过程，如果之前了解过linux的来说那都是很容易的~</p><h3 id="D-1-需要的工具"><a href="#D-1-需要的工具" class="headerlink" title="D.1 需要的工具"></a>D.1 需要的工具</h3><p>编译器: <strong>GCC</strong> <strong>NASM</strong><br>软件环境: <strong>虚拟机</strong>   <strong>Centos6.3</strong>/<strong>ubuntu16.04</strong>   <strong>Bochs2.6.2</strong><br>工具: 虚拟机与主机之间传输文件，我这里选择使用<strong>xftp</strong>软件，远程连接使用的<strong>vscode的插件</strong>/<strong>xshell</strong>，写代码可以选远程连接写或者centos/ubuntu也下载一个vscode</p><blockquote><p>虚拟机我用的vmware，因为之前学习也是这个，其它都是根据书中的环境来的，以方便找出错误</p></blockquote><p>下面就主要安装虚拟机，<strong>编译安装配置运行Bochs</strong>，然后就开始写代码吧</p><hr><h3 id="D-2-配置流程"><a href="#D-2-配置流程" class="headerlink" title="D.2 配置流程"></a>D.2 配置流程</h3><ul><li><p><strong>虚拟机配置</strong> 这里网上教程比较多，就不多说了，我最终是选择在ubuntu16环境下</p></li><li><p><strong>下载Bochs</strong> 这里和教程一样，通过下载源码，配置编译，<a href="http%EF%BC%9A//sourceforge.net/projects/bochs/files/bochs/">bochs-2.6.2.tar.gz下载地址</a>，然后通过xftp或者其它工具发送到虚拟机中</p></li><li><p><strong>解压，进入目录，然后configure、make、make install 三步曲</strong></p></li></ul><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="built_in">cd</span>  tool                    <span class="comment">#进入bochs-2.6.2.tar.gz所在目录</span></span><br><span class="line">tar -xvf bochs-2.6.2.tar.gz <span class="comment">#解压</span></span><br><span class="line"><span class="built_in">cd</span> bochs-2.6.2              <span class="comment">#进入解压后的目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 这里是配置安装路径 支持GDB 支持反汇编，启动io接口调试器，支持x86调试器，使用xwindows 使用x11接口等</span></span><br><span class="line"><span class="comment"># (要注意的是，教程是使用bochs自带的调试，如果要使用需要将--enable-gdb-stub修改成数--enable-debugger，且他们不能同时存在)</span></span><br><span class="line">./configure --prefix=/home/mouse/OS_mouse/tool/bochs --enable-gdb-stub --enable-disasm --enable-iodebug --enable-x86-debugger --with-x --with-x11</span><br><span class="line"></span><br><span class="line"><span class="comment">#生成Makefile文件之后(如果有报错，可以检查报错，看是不是少下了什么依赖)</span></span><br><span class="line"></span><br><span class="line">make install    <span class="comment">#完成安装</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>ps: 注意我这里选择的是GDB调试，所以后面的代码也是与此相关，如果使用Bochs 自带的调试工具，可以参考书中的内容学习，其实都大差不差</p><hr><h3 id="D-3-遇到的问题"><a href="#D-3-遇到的问题" class="headerlink" title="D.3 遇到的问题"></a>D.3 遇到的问题</h3><ul><li>在虚拟机安装Centos的时候，弹出窗口报错<code>This hardware(or a combinationthereof)is not supported byRedHat.For more information onsupported hardware,please referto http://www.redhat.com/hardware</code></li></ul><p>我尝试根据网上的说法，打开笔记本的BIOS界面，打开虚拟化的设置选项(其实我原本就是打开的)，但是尝试后，还是会有当前界面，所以我选择F12先跳过这个界面，如果后面再遇到相关问题再记录</p><ul><li>在配置bochs的时候遇到报错<code>configure: error: in /home/mouse/tool/bochs-2.6.2':configure: error: C++ preprocessor "/lib/cpp" fails sanity check</code>，提示缺少g++的内容，然后准备yum下载，那这里顺便记录一下换镜像源</li></ul><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">#备份原本的内容</span></span><br><span class="line"><span class="built_in">cp</span> /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak</span><br><span class="line"></span><br><span class="line"><span class="comment">#下载新的配置文件</span></span><br><span class="line">curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-6.10.repo</span><br><span class="line"></span><br><span class="line"><span class="comment">#清理并生成新的缓存</span></span><br><span class="line">yum clean all</span><br><span class="line">yum makecache</span><br><span class="line"></span><br><span class="line"><span class="comment">#更新软件(可选)</span></span><br><span class="line"><span class="built_in">sudo</span> yum -y update</span><br><span class="line"></span><br><span class="line"><span class="comment">#安装c++</span></span><br><span class="line"><span class="built_in">sudo</span> yum install gcc-c++</span><br></pre></td></tr></table></figure></div><p>当然，如果有其他报错或者警告，可以查看是缺少了，和上面一下安装对应的东西即可</p><hr><p>ps:其实为了方便我学习其他linux相关，我最终还是选择使用熟悉的环境ubuntu16</p><ul><li>出现<code>undefined reference to 'pthread_create' undefined reference to 'pthread_join'</code>错误，可以参考书中最后给出的解决办法</li></ul><p>这里就不多说了</p><hr><h2 id="E-开始操作–bochs环境-MBR"><a href="#E-开始操作–bochs环境-MBR" class="headerlink" title="E 开始操作–bochs环境 & MBR"></a>E 开始操作–bochs环境 &amp; MBR</h2><p>从这里开始就是重点了(咳咳，其实前面也是)</p><p>下面主要记录我使用的指令，或者编辑的文件(ps：其中的路径，比如<code>/home/mouse/OS_mouse/tool</code> 需要更改成自己的路径)</p><h3 id="E-1-配置bochs"><a href="#E-1-配置bochs" class="headerlink" title="E.1 配置bochs"></a>E.1 配置bochs</h3><p>前面通过配置，然后写一个简单的bochs 支持GDB调试的配置文件，方在bcohs安装路径下即可,比如我<code>/tool/bochs/bochsrc.disk</code><br>(其中<code>ata0-master: type=disk, path="hd60M.img", mode=flat, cylinders=121, heads=16, spt=63</code>需要通过创建虚拟硬盘的工具 bin/bximage 获得)</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">###############################################</span><br><span class="line"># Configuration file <span class="keyword">for</span> Bochs </span><br><span class="line">###############################################</span><br><span class="line"></span><br><span class="line"># 第一步，首先设置 Bochs 在运行过程中能够使用的内存，本例为 <span class="number">32</span>MB。</span><br><span class="line"># 关键字为：megs </span><br><span class="line">megs: <span class="number">32</span> </span><br><span class="line"></span><br><span class="line"># 第二步，设置对应真实机器的 BIOS 和 VGA BIOS。</span><br><span class="line"># 对应两个关键字为：romimage 和 vgaromimage </span><br><span class="line">romimage: file=/home/mouse/OS_mouse/tool/bochs/share/bochs/BIOS-bochs-latest </span><br><span class="line">vgaromimage: file=/home/mouse/OS_mouse/tool/bochs/share/bochs/VGABIOS-lgpl-latest </span><br><span class="line"></span><br><span class="line"># 第三步，设置 Bochs 所使用的磁盘，软盘的关键字为 floppy。</span><br><span class="line"># 若只有一个软盘，则使用 floppya 即可，若有多个，则为 floppya，floppyb…</span><br><span class="line"><span class="meta"># floppya: 1_44=a.img, status=inserted </span></span><br><span class="line"></span><br><span class="line"># 第四步，选择启动盘符。</span><br><span class="line"><span class="meta">#boot: floppy   #默认从软盘启动，将其注释</span></span><br><span class="line">boot: disk      #改为从硬盘启动。我们的任何代码都将直接写在硬盘上，所以不会再有读写软盘的操作。</span><br><span class="line"></span><br><span class="line"># 第五步，设置日志文件的输出。</span><br><span class="line"><span class="built_in">log</span>: bochs.out </span><br><span class="line"></span><br><span class="line"># 第六步，开启或关闭某些功能。</span><br><span class="line"># 下面是关闭鼠标，并打开键盘。</span><br><span class="line">mouse: enabled=<span class="number">0</span> </span><br><span class="line">keyboard_mapping: enabled=<span class="number">1</span>, <span class="built_in">map</span>=/home/mouse/OS_mouse/tool/bochs/share/bochs/keymaps/x11-pc-us.<span class="built_in">map</span> </span><br><span class="line"></span><br><span class="line"># 硬盘设置</span><br><span class="line">ata0: enabled=<span class="number">1</span>, ioaddr1=<span class="number">0x1f0</span>, ioaddr2=<span class="number">0x3f0</span>, irq=<span class="number">14</span> </span><br><span class="line">ata0-master: type=disk, path=<span class="string">"hd60M.img"</span>, mode=flat, cylinders=<span class="number">121</span>, heads=<span class="number">16</span>, spt=<span class="number">63</span></span><br><span class="line">#</span><br><span class="line"></span><br><span class="line"># 下面的是增加的 bochs 对 gdb 的支持，这样 gdb 便可以远程连接到此机器的 <span class="number">1234</span> 端口调试了</span><br><span class="line">gdbstub: enabled=<span class="number">1</span>, port=<span class="number">1234</span>, text_base=<span class="number">0</span>, data_base=<span class="number">0</span>, bss_base=<span class="number">0</span> </span><br><span class="line">################### 配置文件结束 #####################</span><br></pre></td></tr></table></figure></div><p>前面说了其中<strong>硬盘设置</strong>的第二行是通过工具 bin/bximage 获得</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建一个硬盘，类型为flat 大小为60MB 静默模式</span></span><br><span class="line">bin/bximage -hd -mode=<span class="string">"flat"</span> -size=60 -q hd60M.img</span><br></pre></td></tr></table></figure></div><p>执行命令后，会在<code>The following line should appear in your bochsrc:</code>的后面得到一串内容，我们直接更新到<code>bochsrc.disk</code>中即可</p><h3 id="E-2-体验BIOS"><a href="#E-2-体验BIOS" class="headerlink" title="E.2 体验BIOS"></a>E.2 体验BIOS</h3><p>书中对bios(Base Input &amp; Output System)基本输入输出系统的描述很详细，这里就不过多赘述，主要总结几点</p><ol><li>cpu通过<code>cs：f000</code> <code>ip:fff0</code>的预设然后会直接进入 <code>0x7C00</code>地址执行</li><li>如果此扇区末尾的两个字节分别是魔数 <code>0x55</code> 和 <code>0xaa</code>，BIOS 便认为此扇区中确实存在可执行的程序（此程序便是久闻大名的主引导记录 <strong>MBR</strong>）</li></ol><p>下面是书中的一个例子:在屏幕上打印字符串<code>1 MBR</code></p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/mbr.S</span></span><br><span class="line"><span class="comment">; 主引导程序</span></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="meta">SECTION</span> MBR vstart=<span class="number">0x7c00</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">cs</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ds</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">es</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ss</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">fs</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">sp</span>,<span class="number">0x7c00</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">; 清屏利用 0x06 号功能,上卷全部行,则可清屏｡</span></span><br><span class="line"><span class="comment">; ----------------------------------------------------------- </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号:0x06 功能描述:上卷窗口</span></span><br><span class="line"><span class="comment">;------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入: </span></span><br><span class="line"><span class="comment">;AH 功能号= 0x06 </span></span><br><span class="line"><span class="comment">;AL = 上卷的行数(如果为 0,表示全部) </span></span><br><span class="line"><span class="comment">;BH = 上卷行属性</span></span><br><span class="line"><span class="comment">;(CL,CH) = 窗口左上角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;(DL,DH) = 窗口右下角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;无返回值: </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0x600</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0x700</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">0</span> <span class="comment">; 左上角: (0, 0) </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x184f</span> <span class="comment">; 右下角: (80,25), </span></span><br><span class="line"><span class="comment">; VGA 文本模式中,一行只能容纳 80 个字符,共 25 行｡</span></span><br><span class="line"><span class="comment">; 下标从 0 开始,所以 0x18=24,0x4f=79 </span></span><br><span class="line"><span class="keyword">int</span> <span class="number">0x10</span> <span class="comment">; int 0x10 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">;;;;;;;;; 下面这三行代码获取光标位置 ;;;;;;;;; </span></span><br><span class="line"><span class="comment">;.get_cursor 获取当前光标位置,在光标位置处打印字符｡</span></span><br><span class="line"><span class="keyword">mov</span> <span class="number">ah</span>, <span class="number">3</span> <span class="comment">; 输入: 3 号子功能是获取光标位置,需要存入 ah 寄存器</span></span><br><span class="line"><span class="keyword">mov</span> <span class="number">bh</span>, <span class="number">0</span> <span class="comment">; bh 寄存器存储的是待获取光标的页号</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> <span class="number">0x10</span> <span class="comment">; 输出: ch=光标开始行,cl=光标结束行</span></span><br><span class="line"><span class="comment">; dh=光标所在行号,dl=光标所在列号</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;;;;;;;;; 获取光标位置结束 ;;;;;;;;;;;;;;;; </span></span><br><span class="line"></span><br><span class="line"><span class="comment">;;;;;;;;; 打印字符串 ;;;;;;;;;;; </span></span><br><span class="line"><span class="comment">;还是用 10h 中断,不过这次调用 13 号子功能打印字符串</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, message </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bp</span>, <span class="built_in">ax</span> <span class="comment">; es:bp 为串首地址,es 此时同 cs 一致, </span></span><br><span class="line"><span class="comment">; 开头时已经为 sreg 初始化</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 光标位置要用到 dx 寄存器中内容,cx 中的光标位置可忽略</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">5</span> <span class="comment">; cx 为串长度,不包括结束符 0 的字符个数</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0x1301</span> <span class="comment">;子功能号 13 显示字符及属性,要存入 ah 寄存器, </span></span><br><span class="line"><span class="comment">; al 设置写字符方式 ah=01: 显示字符串,光标跟随移动</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0x2</span> <span class="comment">; bh 存储要显示的页号,此处是第 0 页, </span></span><br><span class="line"><span class="comment">; bl 中是字符属性,属性黑底绿字(bl = 02h) </span></span><br><span class="line"><span class="keyword">int</span> <span class="number">0x10</span> <span class="comment">; 执行 BIOS 0x10 号中断</span></span><br><span class="line"><span class="comment">;;;;;;;;; 打字字符串结束 ;;;;;;;;;;;;;;; </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">jmp</span> $ <span class="comment">; 使程序悬停在此</span></span><br><span class="line"></span><br><span class="line">message <span class="built_in">db</span> <span class="string">"1 MBR"</span> <span class="comment">;设置字符串</span></span><br><span class="line"><span class="built_in">times</span> <span class="number">510</span>-($-$$) <span class="built_in">db</span> <span class="number">0</span> <span class="comment">;通过计算当前地址和Section地址的差，也就是填充512字节中除去末尾两个字节以外没有使用的地址为0</span></span><br><span class="line"><span class="built_in">db</span> <span class="number">0x55</span>,<span class="number">0xaa</span>    <span class="comment">;添加魔数</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后就可以通过 <code>nasm</code> 来到文件路径下来编译这个代码，并且放到磁盘中</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get install nasm   <span class="comment">#如果没下载</span></span><br><span class="line"></span><br><span class="line">nasm -o mbr.bin mbr.S   <span class="comment">#mbr.S 是刚刚的文件的名称，编译成功后会生成mbr.bin文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 意思是(if)读取mbr.bin文件 (of)移动到hd60M.img中 (bs)指定512MB (count)指定块大小 (conv)转换方式 建议使用notrunc 方式(不打断文件)</span></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/mbr.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=1 conv=notrunc</span><br></pre></td></tr></table></figure></div><p>然后就会输出<code>记录了1+0 的读入 记录了1+0 的写出 512 bytes copied, 0.000408059 s, 1.3 MB/s</code></p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">bin/bochs -f bochsrc.disk   //通过文件 bochsrc.disk 运行虚拟机，然后回车即可，这个时候虚拟机会弹出另一个窗口</span><br><span class="line"></span><br><span class="line"><span class="comment"># 然后会告诉你让你连接GDB调试 可以直接另开一个终端</span></span><br><span class="line">gdb                         <span class="comment">#打开gdb  后面输入的指令会有前缀(gdb)</span></span><br><span class="line">target remote localhost:1234 <span class="comment">#连接本地端口1234</span></span><br><span class="line"><span class="built_in">continue</span>                    <span class="comment">#进入之后可以选择继续运行</span></span><br></pre></td></tr></table></figure></div><p>如果前面步骤没问题，那么弹出的窗口上便会有字符串<code>1 MBR</code>显示</p><h3 id="E-3-编写MBR"><a href="#E-3-编写MBR" class="headerlink" title="E.3 编写MBR"></a>E.3 编写MBR</h3><p>通过书中从硬件到软件的介绍，现在改写之前的文件，将BIOS的输出改成通过显存输出</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/mbr.S</span></span><br><span class="line"><span class="comment">;主引导程序</span></span><br><span class="line"><span class="comment">; </span></span><br><span class="line"><span class="comment">;LOADER_BASE_ADDR equ 0xA000 </span></span><br><span class="line"><span class="comment">;LOADER_START_SECTOR equ 0x2 </span></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="meta">SECTION</span> MBR vstart=<span class="number">0x7c00</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">cs</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ds</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">es</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ss</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">fs</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">sp</span>,<span class="number">0x7c00</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="number">0xb800</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">gs</span>,<span class="built_in">ax</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;清屏</span></span><br><span class="line"><span class="comment">;利用 0x06 号功能，上卷全部行，则可清屏</span></span><br><span class="line"><span class="comment">; ----------------------------------------------------------- </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号：0x06 功能描述：上卷窗口</span></span><br><span class="line"><span class="comment">;------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入：</span></span><br><span class="line"><span class="comment">;AH 功能号= 0x06 </span></span><br><span class="line"><span class="comment">;AL = 上卷的行数（如果为 0，表示全部）</span></span><br><span class="line"><span class="comment">;BH = 上卷行属性</span></span><br><span class="line"><span class="comment">;(CL,CH) = 窗口左上角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;(DL,DH) = 窗口右下角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;无返回值: </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0600h</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0700h</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">0</span> <span class="comment">; 左上角: (0, 0) </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">184fh</span> <span class="comment">; 右下角: (80,25), </span></span><br><span class="line"><span class="comment">; VGA 文本模式中，一行只能容纳 80 个字符，共 25 行</span></span><br><span class="line"><span class="comment">; 下标从 0 开始，所以 0x18=24，0x4f=79 </span></span><br><span class="line"><span class="keyword">int</span> <span class="number">10h</span> <span class="comment">; int 10h </span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 输出背景色绿色，前景色红色，并且跳动的字符串"1 MBR" </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x00</span>],<span class="string">'1'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x01</span>],<span class="number">0xA4</span> <span class="comment">; A 表示绿色背景闪烁，4 表示前景色为红色</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x02</span>],<span class="string">' '</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x03</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x04</span>],<span class="string">'M'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x05</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x06</span>],<span class="string">'B'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x07</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x08</span>],<span class="string">'R'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x09</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">jmp</span> $ <span class="comment">; 通过死循环使程序悬停在此</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">510</span>-($-$$) <span class="built_in">db</span> <span class="number">0</span> </span><br><span class="line"><span class="built_in">db</span> <span class="number">0x55</span>,<span class="number">0xaa</span></span><br></pre></td></tr></table></figure></div><p>同之前的流程，nasm编译，dd写入硬盘，然后执行，最终可以看到屏幕上有绿色背景闪烁的字符</p><h3 id="E-4-GDB调试简介"><a href="#E-4-GDB调试简介" class="headerlink" title="E.4 GDB调试简介"></a>E.4 GDB调试简介</h3><p>这里直接举例子说功能吧</p><p>首先启动Bochs,然后gdb工具连接，但是<strong>不要输入continue</strong></p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(gdb) x/i 0xffff0       <span class="comment"># x表示查看内存 i表示以指令格式显示 0xffff0表示查看的地址</span></span><br></pre></td></tr></table></figure></div><p>示例输出: <code>0xffff0:     ljmp   $0x3131,$0xf000e05b</code></p><p>其它命令也是同理，这里简单给个表格，<strong>在后面实际使用的时候再列出其它指令</strong></p><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>break *0xffff0</code></td><td>在 <code>0xffff0</code> 设置断点</td></tr><tr><td><code>continue</code> (<code>c</code>)</td><td>继续执行</td></tr><tr><td><code>stepi</code> (<code>si</code>)</td><td>单步执行（进入函数调用）</td></tr><tr><td><code>nexti</code> (<code>ni</code>)</td><td>单步执行（跳过函数调用）</td></tr><tr><td><code>x/xw 0xb00</code></td><td>以十六进制查看 0xb00处的 4 字节</td></tr><tr><td><code>x/dw 0xb00</code></td><td>以十进制查看 0xb00处的 4 字节</td></tr><tr><td><code>x/tw 0xb00</code></td><td>以二进制查看 0xb00处的 4 字节</td></tr><tr><td><code>x/s 0xb00</code></td><td>以字符串格式查看 0xb00处的数据</td></tr></tbody></table><ul><li><strong>实模式调试</strong>：若调试 16 位代码，需先设置架构：</li></ul><h3 id="E-5-硬盘接力"><a href="#E-5-硬盘接力" class="headerlink" title="E.5 硬盘接力"></a>E.5 硬盘接力</h3><p>下面来让MBR从BIOS接手之后来使用硬盘，而使用硬盘即读取磁盘的函数，而我们的MBR受限于512字节，所以需要通过另一个程序来实现: <strong>loader（加载器）</strong><br>最终MBR的使命就是: <strong>将硬盘上的loader加载到内存，然后将接力棒交给它</strong>，同时后面的内核也要用到loader里的数据结构，所以我们选择将loader放在低处，这里和教程一样，选择加载地址为<code>0x900</code></p><p>下面是再次更新(添加硬盘读取的代码)</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/mbr.S</span></span><br><span class="line"><span class="comment">;主引导程序</span></span><br><span class="line"><span class="comment">; </span></span><br><span class="line"><span class="comment">;LOADER_BASE_ADDR equ 0xA000 </span></span><br><span class="line"><span class="comment">;LOADER_START_SECTOR equ 0x2 这里的内容添加在了boot.inc中,下面的%include引用</span></span><br><span class="line"><span class="comment">;------------------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">SECTION</span> MBR vstart=<span class="number">0x7c00</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">cs</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ds</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">es</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ss</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">fs</span>,<span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">sp</span>,<span class="number">0x7c00</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="number">0xb800</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">gs</span>,<span class="built_in">ax</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;清屏</span></span><br><span class="line"><span class="comment">;利用 0x06 号功能，上卷全部行，则可清屏</span></span><br><span class="line"><span class="comment">; ----------------------------------------------------------- </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号：0x06 功能描述：上卷窗口</span></span><br><span class="line"><span class="comment">;------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入：</span></span><br><span class="line"><span class="comment">;AH 功能号= 0x06 </span></span><br><span class="line"><span class="comment">;AL = 上卷的行数（如果为 0，表示全部）</span></span><br><span class="line"><span class="comment">;BH = 上卷行属性</span></span><br><span class="line"><span class="comment">;(CL,CH) = 窗口左上角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;(DL,DH) = 窗口右下角的(X,Y)位置</span></span><br><span class="line"><span class="comment">;无返回值: </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0600h</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0700h</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">0</span> <span class="comment">; 左上角: (0, 0) </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">184fh</span> <span class="comment">; 右下角: (80,25), </span></span><br><span class="line"><span class="comment">; VGA 文本模式中，一行只能容纳 80 个字符，共 25 行</span></span><br><span class="line"><span class="comment">; 下标从 0 开始，所以 0x18=24，0x4f=79 </span></span><br><span class="line"><span class="keyword">int</span> <span class="number">10h</span> <span class="comment">; int 10h </span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 输出背景色绿色，前景色红色，并且跳动的字符串"1 MBR" </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x00</span>],<span class="string">'1'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x01</span>],<span class="number">0xA4</span> <span class="comment">; A 表示绿色背景闪烁，4 表示前景色为红色</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x02</span>],<span class="string">' '</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x03</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x04</span>],<span class="string">'M'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x05</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x06</span>],<span class="string">'B'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x07</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x08</span>],<span class="string">'R'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x09</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">; 这里是主要添加的内容</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>,LOADER_START_SECTOR     <span class="comment">; 起始扇区 lba 地址</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>,LOADER_BASE_ADDR         <span class="comment">; 写入的地址</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">8</span>                        <span class="comment">; 待读入的扇区数，最开始可以是1，但后面会变大，所以一步到位，直接写8了嘿嘿</span></span><br><span class="line"><span class="keyword">call</span> rd_disk_m_16               <span class="comment">; 以下读取程序的起始部分(一个扇区) </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">jmp</span> LOADER_BASE_ADDR</span><br><span class="line"></span><br><span class="line"><span class="comment">; 功能：读取硬盘的n个分区</span></span><br><span class="line"><span class="comment">;----------------------------------------</span></span><br><span class="line"><span class="comment">; eax=LBA 扇区号</span></span><br><span class="line"><span class="comment">; bx=将数据写入的内存地址</span></span><br><span class="line"><span class="comment">; cx=读入的扇区数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;在16位下读取硬盘</span></span><br><span class="line"><span class="symbol">rd_disk_m_16:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="built_in">eax</span> <span class="comment">;备份eax</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">di</span>,<span class="built_in">cx</span>   <span class="comment">;备份cx</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;第 1 步: 设置读取的扇区数</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x1f2</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>,<span class="built_in">cl</span></span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span>       <span class="comment">;读取的扇区数</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,<span class="built_in">esi</span>     <span class="comment">;恢复eax</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;第 2 步: 将LAB地址存入 0x1f3 ~ 0x1f6</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;LBA 地址 7～0 位写入端口 0x1f3 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x1f3</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;LBA 地址 15～8 位写入端口 0x1f4 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cl</span>,<span class="number">8</span> </span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>,<span class="built_in">cl</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x1f4</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line">    <span class="comment">;LBA 地址 23～16 位写入端口 0x1f5 </span></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>,<span class="built_in">cl</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x1f5</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">shr</span> <span class="built_in">eax</span>,<span class="built_in">cl</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">al</span>,<span class="number">0x0f</span>     <span class="comment">;lba 第 24～27 位</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">al</span>,<span class="number">0xe0</span>      <span class="comment">; 设置 7～4 位为 1110,表示 lba 模式</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x1f6</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;第 3 步:向 0x1f7 端口写入读命令,0x20 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>,<span class="number">0x1f7</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">al</span>,<span class="number">0x20</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="built_in">dx</span>,<span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;第 4 步:检测硬盘状态</span></span><br><span class="line"><span class="symbol">.not_ready:</span> </span><br><span class="line">    <span class="comment">;同一端口,写时表示写入命令字,读时表示读入硬盘状态</span></span><br><span class="line">    <span class="keyword">nop</span>         <span class="comment">;空操作，类似于sleep一会</span></span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">al</span>,<span class="built_in">dx</span> </span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">al</span>,<span class="number">0x88</span> <span class="comment">;第 4 位为 1 表示硬盘控制器已准备好数据传输</span></span><br><span class="line">                <span class="comment">;第 7 位为 1 表示硬盘忙</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">al</span>,<span class="number">0x08</span> </span><br><span class="line">    <span class="keyword">jnz</span> .not_ready <span class="comment">;若未准备好,继续等</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;第 5 步:从 0x1f0 端口读数据</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="built_in">di</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">256</span> </span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">dx</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="comment">; di 为要读取的扇区数,一个扇区有 512 字节,每次读入一个字 共需 di*512/2 次,所以 di*256 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1f0</span> </span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.go_on_read:</span> </span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">ax</span>,<span class="built_in">dx</span> </span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">bx</span>],<span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">bx</span>,<span class="number">2</span> </span><br><span class="line">    <span class="keyword">loop</span> .go_on_read</span><br><span class="line">    <span class="keyword">ret</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">510</span>-($-$$) <span class="built_in">db</span> <span class="number">0</span> </span><br><span class="line"><span class="built_in">db</span> <span class="number">0x55</span>,<span class="number">0xaa</span></span><br></pre></td></tr></table></figure></div><p>上面的代码<strong>成功实现了读取硬盘，同时将接力棒交给<code>LOADER_BASE_ADDR</code>地址的 loader 执行</strong></p><p>这里引入了include的概念，所以还需要写一个<code>boot.inc</code>文件，我选择将它放在这个路径下的文件夹include里面</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;/home/mouse/OS_mouse/tool/bochs/mouse/include/boot.inc</span></span><br><span class="line"><span class="comment">;---------- loader &amp; kernel ----------</span></span><br><span class="line"></span><br><span class="line">LOADER_BASE_ADDR <span class="built_in">equ</span> <span class="number">0x900</span>      <span class="comment">;loader addr     loader内存的位置</span></span><br><span class="line">LOADER_START_SECTOR <span class="built_in">equ</span> <span class="number">0x2</span>     <span class="comment">;loader lab addr loader逻辑扇区地址，即第二块扇区</span></span><br></pre></td></tr></table></figure></div><p>同时，使用nasm编译的时候也需要更改指令，<strong>指定一下include的路径</strong>,最后用dd将bin文件写入硬盘</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">nasm -I include/ -o mbr.bin mbr.S</span><br><span class="line"></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/mbr.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=1 conv=notrunc</span><br></pre></td></tr></table></figure></div><p>这个时候如果执行，因为<code>LOADER_BASE_ADDR</code>地址下还什么都没有，所以CPU直接跳到<code>0x900</code>第二问位置，也没有什么结果，可以等下面的简易loader写完之后再尝试运行</p><p>所以下一步就是实现内核加载器(loader)</p><h3 id="E-6-内核加载器（loader）"><a href="#E-6-内核加载器（loader）" class="headerlink" title="E.6 内核加载器（loader）"></a>E.6 内核加载器（loader）</h3><p>同书中所讲，这里实现的loader是只在实模式工作，loader 是要经过实模式到保护模式的过渡，并最终在保护模式下加载内核，等后面学完保护模式后，再来进阶的</p><p>这里loader里面写一个和之前MBR非常接近的代码，只是修改字符串为2 LOADER</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> loader vstart=LOADER_BASE_ADDR</span><br><span class="line"><span class="comment">; 输出背景色绿色,前景色红色,并且跳动的字符串"2 LOADER" </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x00</span>],<span class="string">'2'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x01</span>],<span class="number">0xA4</span> <span class="comment">; A 表示绿色背景闪烁，4 表示前景色为红色</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x02</span>],<span class="string">' '</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x03</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x04</span>],<span class="string">'L'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x05</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x06</span>],<span class="string">'O'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x07</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x08</span>],<span class="string">'A'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x09</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x0a</span>],<span class="string">'D'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x0b</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x0c</span>],<span class="string">'E'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x0d</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x0e</span>],<span class="string">'R'</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">0x0f</span>],<span class="number">0xA4</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">jmp</span> $ <span class="comment">; 通过死循环使程序悬停在此</span></span><br></pre></td></tr></table></figure></div><p>然后还是编译写入，但是这里的写入的扇区是2 <code>seek=2</code>，第0的扇区是MBR，第1个扇区是空的(原作者的爱好)</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">nasm -I include/ -o loader.bin loader.S </span><br><span class="line"></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/loader.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=1 seek=2 conv=notrunc</span><br></pre></td></tr></table></figure></div><p>结果如下<code>记录了0+1 的读入 记录了0+1 的写出 98 bytes copied, 0.000230323 s, 425 kB/s</code></p><p>然后就可以运行看看了，屏幕上会显示”2 loader”,当然这个loader并没有实际意义，只是来验证是否接力成功，最终的任务还是加载内核，从实模式到保护模式。</p><p>所以下面主要学习保护模式</p><hr><h2 id="F-保护模式"><a href="#F-保护模式" class="headerlink" title="F 保护模式"></a>F 保护模式</h2><p>实模式下，访问的都是物理地址，而这样会造成很多问题，所以CPU工程师引入了保护模式，下面主要来简要记录保护模式的相关知识</p><blockquote><ol><li>实模式下<strong>操作系统和用户程序属于同一特权级</strong>，这哥俩平起平坐，没有区别对待。</li><li>用户程序所引用的地址都是指向真实的物理地址，也就是说<strong>逻辑地址等于物理地址</strong></li><li><strong>用户程序可以自由修改段基址</strong>，可以不亦乐乎地访问所有内存，没人拦得住。</li><li>访问<strong>超过 64KB 的内存区域时要切换段基址</strong>，转来转去容易晕乎。</li><li><strong>一次只能运行一个程序</strong>，无法充分利用计算机资源。</li><li>共 20 条地址线，<strong>最大可用内存为 1MB</strong>，这即使在 20 年前也不够用。</li></ol></blockquote><p>CPU 有三种模式：实模式、虚拟 8086 模式、保护模式</p><hr><h3 id="F-1-全局描述符表-如何打开保护模式"><a href="#F-1-全局描述符表-如何打开保护模式" class="headerlink" title="F.1 全局描述符表 & 如何打开保护模式"></a>F.1 全局描述符表 &amp; 如何打开保护模式</h3><p>全局描述符表（Global Descriptor Table，GDT）是保护模式下内存段的登记表，这是不同于实模式的显著特征之一。</p><ul><li><strong>段描述符</strong></li></ul><blockquote><p>段描述符（segment descriptor）是 8 字节（64 位）的结构，用来描述一个段的基址、长度和访问权限。CPU 通过选择子（selector）索引到 GDT/LDT 中的描述符，从而得到段基址和访问控制信息。</p></blockquote><ul><li><strong>全局描述符表 GDT、局部描述符表 LDT 及选择子</strong></li></ul><blockquote><p>到了保护模式下后，由于已经是 32 位地址线和 32 位寄存器啦，任意一寄存器都能够提供 32 位地址，故不需要再将段基址乘以 16 后再与段内偏移地址相加啦，直接用选择子对应的“段描述符中的段基址”加上“段内偏移地址”就是要访问的内存地址。</p></blockquote><ul><li><strong>地址回绕</strong></li></ul><blockquote><p>地址回绕是为了兼容 8086/8088 的实模式。如今我们是在保护模式下，我们需要突破第 20 条地址线（A20）去访问更大的内存空间。而这一切，只有关闭了地址回绕才能实现。而关闭地址回绕，就是上面所说的打开 A20Gate。<br>打开 A20Gate 的方式是极其简单的，将端口 0x92 的第 1 位置 1 就可以了</p></blockquote><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> <span class="built_in">al</span>,<span class="number">0x92</span></span><br><span class="line"><span class="keyword">or</span> a1,<span class="number">0000_0010B</span></span><br><span class="line"><span class="keyword">out</span> <span class="number">0x92</span>,a1</span><br></pre></td></tr></table></figure></div><ul><li><strong>保护模式的开关，CR0 寄存器的 PE 位</strong></li></ul><blockquote><p>PE 为 0 表示在实模式下运行，PE 为 1 表示在保护模式下运行,所以</p></blockquote><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>,<span class="built_in">cr0</span></span><br><span class="line"><span class="keyword">or</span> <span class="built_in">eax</span>,<span class="number">0x00000001</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cr0</span>,<span class="built_in">eax</span></span><br></pre></td></tr></table></figure></div><hr><h3 id="F-2-打开保护模式"><a href="#F-2-打开保护模式" class="headerlink" title="F.2 打开保护模式"></a>F.2 打开保护模式</h3><ul><li>首先要修改<strong>mbr.S</strong>文件中读入的扇区大小，因为loader.bin的大小会超过512字节,所以将 <code>mov cx,1</code>修改成 ``mov cx,4`<br><strong>注意在后期如果loader.bin的文件大小超过了mbr读取的扇区数，也要修改这个参数,同时在写入磁盘的时候也要注意大小，之前我们不是已经改成8了嘛</strong></li></ul><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/mbr.S</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 略...... ;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>,LOADER_START_SECTOR     <span class="comment">; 起始扇区 lba 地址</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>,LOADER_BASE_ADDR         <span class="comment">; 写入的地址</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">8</span>                        <span class="comment">; 待读入的扇区数</span></span><br><span class="line"><span class="keyword">call</span> rd_disk_m_16               <span class="comment">; 以下读取程序的起始部分(一个扇区) </span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 略...... ;</span></span><br></pre></td></tr></table></figure></div><hr><ul><li>下一个修改文件是<strong>boot.inc</strong>，因为loader.S文件中用到的配置都是定义在这个文件中的符号，修改如下</li></ul><p><strong>其中equ 是 nasm 提供的伪指令，意为 equal，即等于，用于给表达式起个意义更明确的符号名，其指令格式是</strong>：<code>符号名称 equ 表达式</code></p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;/home/mouse/OS_mouse/tool/bochs/mouse/boot.inc</span></span><br><span class="line"><span class="comment">;--------------------- loader &amp; kernel -------------------------</span></span><br><span class="line"></span><br><span class="line">LOADER_BASE_ADDR <span class="built_in">equ</span> <span class="number">0x900</span>      <span class="comment">;loader addr     loader内存的位置</span></span><br><span class="line">LOADER_START_SECTOR <span class="built_in">equ</span> <span class="number">0x2</span>     <span class="comment">;loader lab addr loader逻辑扇区地址，即第二块扇区</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;--------------------- gdt 描述符属性 ------------------------- </span></span><br><span class="line">DESC_G_4K <span class="built_in">equ</span>   <span class="number">1_00000000000000000000000b</span>      <span class="comment">;第23位G段，设置粒度，段界限的单位值为4k</span></span><br><span class="line">DESC_D_32 <span class="built_in">equ</span>   <span class="number">1_0000000000000000000000b</span>       <span class="comment">;第22位D/B位，表示地址值用32位EIP寄存器，操作数与指令码32位</span></span><br><span class="line">DESC_L <span class="built_in">equ</span>      <span class="number">0_000000000000000000000b</span>        <span class="comment">;64 位代码标记,此处标记为 0 便可</span></span><br><span class="line">DESC_AVL <span class="built_in">equ</span>    <span class="number">0_00000000000000000000b</span>         <span class="comment">;CPU 不用此位,暂置为 0 </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">DESC_LIMIT_CODE2    <span class="built_in">equ</span> <span class="number">1111_0000000000000000b</span> </span><br><span class="line">DESC_LIMIT_DATA2    <span class="built_in">equ</span> DESC_LIMIT_CODE2 </span><br><span class="line">DESC_LIMIT_VIDEO2   <span class="built_in">equ</span> <span class="number">0000_000000000000000b</span></span><br><span class="line"></span><br><span class="line">DESC_P      <span class="built_in">equ</span> <span class="number">1_000000000000000b</span>      <span class="comment">;判断是否存在于内存</span></span><br><span class="line"></span><br><span class="line">DESC_DPL_0  <span class="built_in">equ</span> <span class="number">00_0000000000000b</span>       </span><br><span class="line">DESC_DPL_1  <span class="built_in">equ</span> <span class="number">01_0000000000000b</span> </span><br><span class="line">DESC_DPL_2  <span class="built_in">equ</span> <span class="number">10_0000000000000b</span> </span><br><span class="line">DESC_DPL_3  <span class="built_in">equ</span> <span class="number">11_0000000000000b</span></span><br><span class="line"></span><br><span class="line">DESC_S_CODE <span class="built_in">equ</span> <span class="number">1_000000000000b</span></span><br><span class="line">DESC_S_DATA <span class="built_in">equ</span> DESC_S_CODE </span><br><span class="line">DESC_S_sys  <span class="built_in">equ</span> <span class="number">0_000000000000b</span> </span><br><span class="line"></span><br><span class="line">DESC_TYPE_CODE <span class="built_in">equ</span> <span class="number">1000_00000000b</span>   <span class="comment">;x=1,c=0,r=0,a=0 代码段是可执行的，非一致性，不可读，已访问位 a 清 0 </span></span><br><span class="line">DESC_TYPE_DATA <span class="built_in">equ</span> <span class="number">0010_00000000b</span>   <span class="comment">;x=0,e=0,w=1,a=0 数据段是不可执行的，向上扩展的，可写，已访问位 a 清 0 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">;代码段描述符高位4字节初始化 (0x00共8位 &lt;&lt;24 共32位初始化0) </span></span><br><span class="line">DESC_CODE_HIGH4 <span class="built_in">equ</span> (<span class="number">0x00</span> &lt;&lt; <span class="number">24</span>)    + DESC_G_4K + DESC_D_32 + DESC_L  + DESC_AVL + DESC_LIMIT_CODE2  + DESC_P+DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + <span class="number">0x00</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;数据段描述符高位4字节初始化</span></span><br><span class="line">DESC_DATA_HIGH4 <span class="built_in">equ</span> (<span class="number">0x00</span> &lt;&lt; <span class="number">24</span>)    + DESC_G_4K + DESC_D_32 + DESC_L  + DESC_AVL + DESC_LIMIT_DATA2  +  DESC_P + DESC_DPL_0 + DESC_S_DATA +  DESC_TYPE_DATA + <span class="number">0x00</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;显存段描述符高位4字节初始化</span></span><br><span class="line">DESC_VIDEO_HIGH4 <span class="built_in">equ</span> (<span class="number">0x00</span>  &lt;&lt; <span class="number">24</span>)   + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + <span class="number">0x0b</span> </span><br><span class="line"><span class="comment">;这里注意末尾添加的是0X0b，而书中添加的0X00(初始化显存会失败)</span></span><br><span class="line"><span class="comment">;某些模拟器（如 Bochs）或硬件可能对显存地址有严格的要求，必须显式设置基地址的高位为 0x0B才能正确访问显存。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;--------------- 选择子属性 RPL 特权级比较是否允许访问  第2位TI 0表示GDT 1表示LDT    第3-15位索引值 ------------------------- </span></span><br><span class="line">RPL0 <span class="built_in">equ</span> <span class="number">00b</span> </span><br><span class="line">RPL1 <span class="built_in">equ</span> <span class="number">01b</span> </span><br><span class="line">RPL2 <span class="built_in">equ</span> <span class="number">10b</span> </span><br><span class="line">RPL3 <span class="built_in">equ</span> <span class="number">11b</span> </span><br><span class="line">TI_GDT <span class="built_in">equ</span> <span class="number">000b</span> </span><br><span class="line">TI_LDT <span class="built_in">equ</span> <span class="number">100b</span></span><br></pre></td></tr></table></figure></div><p>代码中具体的说明书中有详细介绍</p><hr><ul><li>最后一个文件是<strong>loader.S</strong>，如下</li></ul><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> loader vstart=LOADER_BASE_ADDR</span><br><span class="line">LOADER_STACK_TOP <span class="built_in">equ</span> LOADER_BASE_ADDR</span><br><span class="line"><span class="keyword">jmp</span> loader_start</span><br><span class="line"></span><br><span class="line"><span class="comment">;1.构建gdt及其内部的描述符,拆分为上4字节，下4字节</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">GDT_BASE:</span>   <span class="built_in">dd</span> <span class="number">0x00000000</span> </span><br><span class="line">            <span class="built_in">dd</span> <span class="number">0x00000000</span> </span><br><span class="line"><span class="symbol">CODE_DESC:</span>  <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">            <span class="built_in">dd</span> DESC_CODE_HIGH4    </span><br><span class="line"><span class="symbol">DATA_STACK_DESC:</span>    <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">                    <span class="built_in">dd</span> DESC_DATA_HIGH4 </span><br><span class="line"><span class="symbol">VIDEO_DESC:</span> <span class="built_in">dd</span> <span class="number">0x80000007</span>               <span class="comment">;limit=(0xbffff-0xb8000)/4k=0x7 </span></span><br><span class="line">            <span class="built_in">dd</span> DESC_VIDEO_HIGH4         <span class="comment">;此时 dpl 为 0 </span></span><br><span class="line">GDT_SIZE    <span class="built_in">equ</span> $ - GDT_BASE </span><br><span class="line">GDT_LIMIT   <span class="built_in">equ</span> GDT_SIZE - <span class="number">1</span> </span><br><span class="line"><span class="built_in">times</span> <span class="number">60</span> <span class="built_in">dq</span> <span class="number">0</span> <span class="comment">; 此处预留 60 个描述符的空位times 是 nasm 提供的伪指令，用来重复执行 times 后面表达式(编译器执行)</span></span><br><span class="line"></span><br><span class="line">SELECTOR_CODE <span class="built_in">equ</span> (<span class="number">0x0001</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 </span><br><span class="line"> <span class="comment">; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 </span></span><br><span class="line">SELECTOR_DATA <span class="built_in">equ</span> (<span class="number">0x0002</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line">SELECTOR_VIDEO <span class="built_in">equ</span> (<span class="number">0x0003</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;以下是 gdt 的指针，前 2 字节是 gdt 界限，后 4 字节是 gdt 起始地址</span></span><br><span class="line">gdt_ptr <span class="built_in">dw</span> GDT_LIMIT </span><br><span class="line"><span class="built_in">dd</span> GDT_BASE </span><br><span class="line">loadermsg <span class="built_in">db</span> <span class="string">'2 loader in real.'</span> </span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">loader_start:</span> </span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号:0x13 功能描述:打印字符串</span></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入: </span></span><br><span class="line"><span class="comment">;AH 子功能号=13H </span></span><br><span class="line"><span class="comment">;BH = 页码</span></span><br><span class="line"><span class="comment">;BL = 属性(若 AL=00H 或 01H) </span></span><br><span class="line"><span class="comment">;CX=字符串长度</span></span><br><span class="line"><span class="comment">;(DH､ DL)=坐标(行 列､ ) </span></span><br><span class="line"><span class="comment">;ES:BP=字符串地址</span></span><br><span class="line"><span class="comment">;AL=显示输出方式</span></span><br><span class="line"><span class="comment">; 0—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 1—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置改变</span></span><br><span class="line"><span class="comment">; 2—字符串中含显示字符和显示属性。显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 3—字符串中含显示字符和显示属性。显示后，光标位置改变</span></span><br><span class="line"><span class="comment">;无返回值</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">sp</span>, LOADER_BASE_ADDR </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bp</span>, loadermsg   <span class="comment">; ES:BP = 字符串地址</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">17</span>          <span class="comment">; CX = 字符串长度</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0x1301</span>      <span class="comment">; AH = 13, AL = 01h </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0x001f</span>      <span class="comment">; 页号为 0(BH = 0) 蓝底粉红字(BL = 1fh) </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1800</span> </span><br><span class="line"><span class="keyword">int</span> <span class="number">0x10</span>            <span class="comment">; 10h 号中断</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;-------------------- 准备进入保护模式 ------------------------------- </span></span><br><span class="line"><span class="comment">;1 打开 A20 </span></span><br><span class="line"><span class="comment">;2 加载 gdt </span></span><br><span class="line"><span class="comment">;3 将 cr0 的 pe 位置 1 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------- 1. 打开 A20 ---------------- </span></span><br><span class="line"><span class="keyword">in</span> <span class="built_in">al</span>,<span class="number">0x92</span> </span><br><span class="line"><span class="keyword">or</span> <span class="built_in">al</span>,<span class="number">0000_0010B</span> </span><br><span class="line"><span class="keyword">out</span> <span class="number">0x92</span>,<span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------- 2. 加载 GDT ---------------- </span></span><br><span class="line"><span class="keyword">lgdt</span> [gdt_ptr] </span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------- 3. cr0 第 0 位置 1 ---------------- </span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="built_in">cr0</span> </span><br><span class="line"><span class="keyword">or</span> <span class="built_in">eax</span>, <span class="number">0x00000001</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">cr0</span>, <span class="built_in">eax</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">jmp</span> <span class="built_in">dword</span> SELECTOR_CODE:p_mode_start <span class="comment">; 刷新流水线,这个下一节会具体说</span></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>] </span><br><span class="line"><span class="symbol">p_mode_start:</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_DATA </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ds</span>, <span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">es</span>, <span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ss</span>, <span class="built_in">ax</span> </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">esp</span>,LOADER_STACK_TOP </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_VIDEO </span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">gs</span>, <span class="built_in">ax</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">160</span>], <span class="string">'P'</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">jmp</span> $</span><br></pre></td></tr></table></figure></div><hr><p>最后还是和之前一样编译运行，然后会在屏幕上看到第一行是原本在mbr.S文件中打印的字符，第二行的字符PZL是在保护模式打印的，最下面的字符串是在实模式用0x10中断打印的</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#编译</span></span><br><span class="line">nasm -I include/ -o mbr.bin mbr.S</span><br><span class="line">nasm -I include/ -o loader.bin loader.S</span><br><span class="line"></span><br><span class="line"><span class="comment">#写入</span></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/mbr.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=1 seek=0 conv=notrunc</span><br><span class="line"></span><br><span class="line"><span class="comment">#这里写入的count=2 是因为我们的bin文件有634bytes，超过512*1，如果不修改写入的时候会被截短，那么屏幕上将不会有显示，所以后期也要注意文件大小的问题</span></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/loader.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=2 seek=2 conv=notrunc</span><br><span class="line"></span><br><span class="line"><span class="comment">#开始运行</span></span><br><span class="line"><span class="built_in">cd</span> ..</span><br><span class="line">bin/bochs -f bochsrc.disk</span><br><span class="line"><span class="comment">#-------------</span></span><br><span class="line">gdb</span><br><span class="line">(gdb) target remote localhost:1234</span><br><span class="line">(gdb) <span class="built_in">continue</span> </span><br></pre></td></tr></table></figure></div><hr><h3 id="F-3-处理器微架构简介"><a href="#F-3-处理器微架构简介" class="headerlink" title="F.3 处理器微架构简介"></a>F.3 处理器微架构简介</h3><h4 id="F3-1-流水线"><a href="#F3-1-流水线" class="headerlink" title="F3.1 流水线"></a>F3.1 流水线</h4><p>流水线是CPU 中的一个非常重要的技术,可以简单理解成CPU在并行执行任务，<strong>在”同一时间”用多级流水线将任务拆分，以高效率执行</strong>，举个简单例子：</p><blockquote><p>指令执行单元 EU 是执行指令的唯一部件，一次只能执行一个指令，<br>单核 CPU 的情况下，只有一个指令处于执行中。CPU 中的各部分也是同时只能做一件事，但它们就像身体器官一样，也是在并行工作，相当于多个“人手”。CPU 的指令执行过程分为取指令、译码、执行三个步骤。<br>每个步骤都是独立执行的，CPU 可以一边执行指令，一边取指令，一边译码。CPU 中的时序不是秒，对 CPU 来说，秒就是天文数字。它的时序是时钟周期。</p></blockquote><hr><h4 id="F3-2-乱序执行"><a href="#F3-2-乱序执行" class="headerlink" title="F3.2 乱序执行"></a>F3.2 乱序执行</h4><p>乱序执行，是指在 CPU 中运行的指令并不按照代码中的顺序执行，而是按照一定的策略打乱顺序执行，也许后面的指令先执行，当然，得保证指令之间不具备相关性。</p><p>举个书中的例子:</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>,[<span class="number">0x1234</span>]</span><br><span class="line"><span class="keyword">add</span> <span class="built_in">eax</span>,<span class="built_in">ebx</span></span><br></pre></td></tr></table></figure></div><p>这里的两条指令，因为ebx与eax相加需要依赖eax的值，所以必须等待第一步的mov操作结束，而相对内存访问比较慢，只能等着，顺序执行mov，然后是add<br>但是如果换一个代码:</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>,[<span class="number">0x1234</span>]</span><br><span class="line"><span class="keyword">add</span> <span class="built_in">ecx</span>,<span class="built_in">ebx</span></span><br></pre></td></tr></table></figure></div><p>这个时候会发现add指令不依赖第一步的eax，所以cpu可以在执行mov指令的等待过程中去执行先执行第二步的add指令。由于第 2 步不依赖第 1 步，</p><p>总结一下，<strong>乱序执行的好处就是后面的操作可以放到前面来做，利于装载到流水线上提高效率</strong>。</p><hr><h4 id="F3-3-缓存"><a href="#F3-3-缓存" class="headerlink" title="F3.3 缓存"></a>F3.3 缓存</h4><blockquote><p>缓存是 20 世纪最大的发明，其原理是用一些存取速度较快的存储设备作为数据缓冲区，避免频繁访问速度较慢的低速存储设备，归根结底的原因是低速存储设备是整个系统的瓶颈，缓存用来缓解“瓶颈设备”的压力。</p></blockquote><p>CPU的缓存选择使用SRAM(即静态随机访问存储器),因为相对于CPU，DRAM(动态随机访问存储器)还是太慢了</p><blockquote><p>什么时候能缓存呢？可以根据程序的局部性原理采取缓存策略。局部性原理是：程序 90%的时间都运行在程序中 10%的代码上。<br>局部性分为以下两个方面。<br>一方面是时间局部性：最近访问过的指令和数据，在将来一段时间内依然经常被访问。<br>另一方面是空间局部性：靠近当前访问内存空间的内存地址，在将来一段时间也会被访问。</p></blockquote><hr><h4 id="F3-4-分支预测"><a href="#F3-4-分支预测" class="headerlink" title="F3.4 分支预测"></a>F3.4 分支预测</h4><blockquote><p>CPU 中的指令是在流水线上执行。分支预测，是指当处理器遇到一个分支指令时，是该把分支左边的指令放到流水线上，还是把分支右边的指令放在流水线上呢？<br>如 C 语言程序中的 if、switch、for 等语言结构，编译器将它们编译成汇编代码后，在汇编一级来说，这些结构都是用跳转指令来实现的，所以，汇编语言中的无条件跳转指令很丰富，以至于称之为跳转指令“族”，多得足矣应对各种转移方式。</p></blockquote><p>下面是一个例子,先创建一个while.c文件,内容如下：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///home/mouse/OS_mouse/tool/bochs/mouse/drafts/while.c</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">main</span> <span class="params">()</span> </span><br><span class="line">{ </span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">while</span> (i &lt; <span class="number">10</span>) </span><br><span class="line">    { </span><br><span class="line">        i++; </span><br><span class="line">    } </span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后通过指令，用gcc生成对应的汇编文件,最后查看while.S</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gcc -S -o while.S while.c   <span class="comment"># -S表示编译成汇编语言，不进行汇编和链接</span></span><br><span class="line"><span class="built_in">cat</span> while.S                 <span class="comment"># 查看该文件</span></span><br></pre></td></tr></table></figure></div><p>这里不是源文件，我将部分注释添加到了文件里面，方便理解，同时，这个生成的汇编语言并不是我们熟悉的 Intel 语法(ps:其实我都不熟悉)，而是 AT&amp;T 语法</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; -------- 声明代码段，导出main函数符号 --------</span></span><br><span class="line"><span class="meta"> .file</span> <span class="string">"while.c"</span></span><br><span class="line"><span class="meta"> .text</span></span><br><span class="line"><span class="meta"> .globl</span> main</span><br><span class="line"><span class="meta"> .type</span> main, @function</span><br><span class="line"></span><br><span class="line"><span class="comment">; main函数入口地址</span></span><br><span class="line"><span class="symbol">main:</span></span><br><span class="line"><span class="symbol">.LFB0:</span>      <span class="comment">;标签，通常标识函数的开始</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 创建堆栈框架</span></span><br><span class="line"><span class="meta"> .cfi_startproc</span></span><br><span class="line"> pushq %rbp</span><br><span class="line"><span class="meta"> .cfi_def_cfa_offset</span> <span class="number">16</span></span><br><span class="line"><span class="meta"> .cfi_offset</span> <span class="number">6</span>, -<span class="number">16</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 为int i = 0 在栈中分配空间</span></span><br><span class="line"> <span class="keyword">movq</span> %rsp, %rbp</span><br><span class="line"><span class="meta"> .cfi_def_cfa_register</span> <span class="number">6</span></span><br><span class="line"> movl <span class="number">$0</span>, -<span class="number">4</span>(%rbp)</span><br><span class="line"></span><br><span class="line"><span class="comment">;无条件跳转到标签.L2</span></span><br><span class="line"> <span class="keyword">jmp</span> .L2</span><br><span class="line"><span class="symbol">.L3:</span>                    <span class="comment">;这里是while的循环体</span></span><br><span class="line"> addl <span class="number">$1</span>, -<span class="number">4</span>(%rbp)</span><br><span class="line"><span class="symbol">.L2:</span>                    <span class="comment">;这里是while循环的条件表达式</span></span><br><span class="line"> cmpl <span class="number">$9</span>, -<span class="number">4</span>(%rbp)</span><br><span class="line"> <span class="keyword">jle</span> .L3                <span class="comment">;比较结果小于9就跳转到.L3</span></span><br><span class="line"> <span class="keyword">nop</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">;退出栈帧等</span></span><br><span class="line"> popq %rbp</span><br><span class="line"><span class="meta"> .cfi_def_cfa</span> <span class="number">7</span>, <span class="number">8</span></span><br><span class="line"> <span class="keyword">ret</span>                    <span class="comment">;退出main函数</span></span><br><span class="line"><span class="meta"> .cfi_endproc</span></span><br><span class="line"><span class="symbol">.LFE0:</span>                  <span class="comment">;局部标签，函数结束标识</span></span><br><span class="line"><span class="meta"> .size</span> main, .-main</span><br><span class="line"><span class="meta"> .ident</span> <span class="string">"GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"</span></span><br><span class="line"><span class="meta"> .section</span> .note<span class="number">.</span>GNU-stack,<span class="string">""</span>,@progbits</span><br></pre></td></tr></table></figure></div><blockquote><p><strong>如果分支预测错怎么办</strong><br>也就是说，当前指令执行结果与预测的结果不同，这也没关系，只要将流水线清空就好了。因为处于执行阶段的是当前指令，即分支跳转指令。处于“译码”“取指”的是尚未执行的指令，即错误分支上的指令。只要错误分支上的指令还没到执行阶段就可以挽回，所以，直接清空流水线就是把流水线上错误分支上的指令清掉，再把正确分支上的指令加入到流水线，只是清空流水线代价比较大。</p></blockquote><hr><h4 id="F3-5-清空流水线"><a href="#F3-5-清空流水线" class="headerlink" title="F3.5 清空流水线"></a>F3.5 清空流水线</h4><p>之前的loader.S 文件中有个无跳转指令 <code>jmp dword SELECTOR_CODE:p_mode_start</code> 用来清空流水线</p><blockquote><p>段描述符缓冲寄存器在 CPU 的实模式和保护模式中都同时使用，在不重新引用一个段时，段描述符缓冲寄存器中的内容是不会更新的，无论是在实模式，还是保护模式下，CPU 都以段描述符缓冲寄存器中的内容为主。实模式进入保护模式时，由于段描述符缓冲寄存器中的内容仅仅是实模式下的 20 位的段基址，很多属性位都是错误的值，这对保护模式来说必然会造成错误，所以需要马上更新段描述符缓冲寄存器，也就是要想办法往相应段寄存器中加载选择子。</p></blockquote><p>所以，解决问题的关键就是既要改变代码段描述符缓冲寄存器的值，又要清空流水线。而 jmp 指令有清空流水线的神奇功效，所以<strong>使用远跳转指令清空流水线，更新段描述符缓冲寄存器</strong></p><hr><h3 id="F-4-保护模式中的”保护”"><a href="#F-4-保护模式中的”保护”" class="headerlink" title="F.4 保护模式中的”保护”"></a>F.4 保护模式中的”保护”</h3><p><strong>保护模式中的保护二字主要体现在段描述符的属性字段中</strong>。每个字段都不是多余的。这些属性只是用来描述一块内存的性质，是用来给 CPU 做参考的，当有实际动作在这片内存上发生时，CPU 用这些属性来检查动作的合法性，从而起到了保护的作用</p><blockquote><p><strong>选择子的保护</strong> 当引用一个内存段时，实际上就是往段寄存器中加载个选择子，为了避免出现非法引用内存段的情况，在这时候，处理器会在以下几方面做出检查</p></blockquote><ul><li><strong>根据选择子的值验证段描述符是否超越界限</strong></li><li><strong>检查段的类型-段描述符中还有个 type 字段</strong></li><li><strong>检查盾是否存在-即检查P位是否为1</strong></li></ul><blockquote><p><strong>代码段和数据段的保护</strong></p></blockquote><p>对于代码段和数据段来说，CPU 每访问一个地址，都要确认该地址不能超过其所在内存段的范围</p><blockquote><p><strong>栈段的保护</strong></p></blockquote><p>CPU 对数据段的检查，其中一项就是看地址是否超越段界限</p><hr><h2 id="G-保护模式进阶"><a href="#G-保护模式进阶" class="headerlink" title="G 保护模式进阶"></a>G 保护模式进阶</h2><p>之前过多还是偏于理论基础，都是在为了后面的操作系统打基础，从现在开始，我们的代码将在保护模式下工作，除了开启虚拟内存外，我们还会接触到其他硬件，从这一刻起，现在才算开始了真正的操作系统学习之旅</p><h3 id="G-1-获取物理内存容量"><a href="#G-1-获取物理内存容量" class="headerlink" title="G.1 获取物理内存容量"></a>G.1 获取物理内存容量</h3><p>保护模式最“大”的特点就是寻址空间“大”，在进入保护模式之后，我们将接触到虚拟内存、内存管理等。但这些和内存有关的概念都建立在物理内存之上，无论理论概念说得多高大上，最终也要在物理内存上落实行动。为了在后期做好内存管理工作，<strong>现在的目的就是知道自己有多少内容</strong></p><ul><li><strong>Linux下获取内存的方法</strong>:比如在 Linux 2.6 内核中，是用 detect_memory 函数来获取内存容量的。其函数在本质上是通过调用 BIOS 中断 0x15 实现</li></ul><p>这里有三个子功能,子功能号要存放到寄存器 EAX 或 AX 中，如下。</p><blockquote><p>EAX=0xE820：遍历主机上全部内存。<br>AX=0xE801： 分别检测低 15MB 和 16MB～4GB 的内存，最大支持 4GB。<br>AH=0x88：最多检测出 64MB 内存，实际内存超过此容量也按照 64MB 返回。</p></blockquote><hr><h4 id="G1-1-获取方法简介"><a href="#G1-1-获取方法简介" class="headerlink" title="G1.1 获取方法简介"></a>G1.1 获取方法简介</h4><p>ARDS结构(地址范围描述符- Address Range Descriptor Structure)，格式见下表</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/OS/os_img_001.webp" width="700"><p>其中的type字段用来描述这段内存的类型，即是否可以被操作系统使用，还是保留起来不能使用</p><blockquote><p><strong>下面介绍调用BIOS中断0x15的0xe820需要的参数</strong>:</p></blockquote><ul><li><strong>调用前输入</strong></li></ul><ol><li><strong>EAX</strong>:子功能号，这里输入0XE820</li><li><strong>EBX:ARDS</strong>后续值，因为没执行一次中断只返回一中类型的ARDS结构，所以要记录下一个待返回的内存ARDS，第一次调用必须写入0，每次返回后，BIOS会更新该值</li><li><strong>ES:DI</strong>：ARDS缓冲区，BIOS 将获取到的内存信息写入此寄存器指向的内存，每次都以 ARDS 格式返回</li><li><strong>ECX</strong>:ARDS结构的字节大小，用来指定BIOS写入的字节数(调用者和 BIOS 都同时支持的大小是 20 字节，将来也许会扩展此结构)</li><li><strong>EDX</strong>:固定签名标记<code>0x534d4150</code></li></ol><ul><li><strong>调用后输出</strong></li></ul><ol><li><strong>CF位</strong>：若CF为0则表示调用为出错，为1表示调用出错</li><li><strong>EAX</strong>：字符串0x534d4150(和之前的签名标记对应)</li><li><strong>ES:DI</strong>: ARDS缓冲区地址，通输入值是一样的,返回时这个地址已经被BIOS填充了内存信息</li><li><strong>ECX</strong>: BIOS 写入到 ES:DI 所指向的 ARDS 结构中的字节数，BIOS 最小写入 20 字节</li><li><strong>EBS</strong>：下一个 ARDS 的位置。每次中断返回后，BIOS 会更新此值，BIOS 通过此值可以找到下一个待返回的 ARDS 结构，咱们不需要改变 EBX 的值，下一次中断调用时还会用到它。在 CF 位为 0 的情况下，若返回后的 EBX 值为 0，表示这是最后一个 ARDS 结构</li></ol><blockquote><p><strong>下面介绍调用BIOS中断0x15的0xe801需要的参数</strong>:</p></blockquote><p>另一个获取内存容量的方法是 BIOS0x15 中断的子功能 0xE801。<br>此方法虽然简单，但功能也不强大，<strong>最大只能识别 4GB 内存</strong>，不过这对咱们 32 位地址总线足够了。<br>稍微有点不便的是此方法检测到的内存是分别存放到两组寄存器中的。低于 15MB 的内存以 1KB 为单位大小来记录，16~4GB则是用64kb为单位。<br>其中保留了1MB不使用也是为了兼容以前的设备(当做缓冲区)</p><ul><li><strong>调用前输入</strong></li></ul><ol><li><strong>AX</strong>： 子功能号:0xe801</li></ol><ul><li><strong>调用后输出</strong></li></ul><ol><li><strong>CF位</strong>：若为0则调用未出错，1表示出错</li><li><strong>AX</strong>：以1kb为单位，只显示15MB以下的内存容量，即最大值为0x3c00 <code>0x3c00*1024 = 15MB</code></li><li><strong>BX</strong>：以64k为单位，内存空间 16MB～4GB 中连续的单位数量，即内存大小为 <code>BX*64*1024</code>字节</li><li><strong>CX</strong>：同AX</li><li><strong>DX</strong>：同BX</li></ol><blockquote><p><strong>下面介绍调用BIOS中断0x15的0x88需要的参数</strong>:</p></blockquote><p>该方法使用最简单，但功能也最简单，<strong>简单到只能识别最大 64MB 的内存</strong>。即使内存容量大于 64MB，也只会显示 63MB<br>大家可以自己在bochs中试验下。为什么只显示到63MB呢？因为此中断只会显示1MB之上的内存，不包括这1MB，所以我们在使用的时候需要记得得加上 1MB。</p><ul><li><strong>调用前输入</strong></li></ul><ol><li><strong>AH</strong>： 子功能号:0x88</li></ol><ul><li><strong>调用后输出</strong></li></ul><ol><li>CF位：输出0表示调用未出错，1表示出错</li><li>AX  : 以1kb为单位大小，内存1MB以上的连续单位数量；内存大小=AX*1024字节 + 1MB</li></ol><hr><h4 id="G1-2-代码实操"><a href="#G1-2-代码实操" class="headerlink" title="G1.2 代码实操"></a>G1.2 代码实操</h4><p>这里我没有完全按照书中的来写(保留了之前打印的字符串),基本每行代码都有注释(其实因为自己汇编还是太烂了，多写点防止忘记)</p><p>这次的新代码主要是添加了三种获取内存大小的方法(上一小节介绍的三种)，<strong>并将结果保存在<code>total_mem_bytes</code>里面，通过填充，保证它的地址为<code>0xb00</code></strong>,等会可以使用GDB调试查看内存大小(32MB),至于为什么是32MB，是因为在<code>bochsrc.disk</code>文件中配置的:<code>megs: 32</code></p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> loader vstart=LOADER_BASE_ADDR</span><br><span class="line">LOADER_STACK_TOP <span class="built_in">equ</span> LOADER_BASE_ADDR</span><br><span class="line"><span class="keyword">jmp</span> loader_start</span><br><span class="line"></span><br><span class="line"><span class="comment">; 构建gdt及其内部的描述符,拆分为上4字节，下4字节</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">GDT_BASE:</span>   <span class="built_in">dd</span> <span class="number">0x00000000</span>       <span class="comment">;空描述段</span></span><br><span class="line">            <span class="built_in">dd</span> <span class="number">0x00000000</span>       </span><br><span class="line"><span class="symbol">CODE_DESC:</span>  <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">            <span class="built_in">dd</span> DESC_CODE_HIGH4    </span><br><span class="line"><span class="symbol">DATA_STACK_DESC:</span>    <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">                    <span class="built_in">dd</span> DESC_DATA_HIGH4 </span><br><span class="line"><span class="symbol">VIDEO_DESC:</span> <span class="built_in">dd</span> <span class="number">0x80000007</span>               <span class="comment">;limit=(0xbffff-0xb8000)/4k=0x7 </span></span><br><span class="line">            <span class="built_in">dd</span> DESC_VIDEO_HIGH4         <span class="comment">;此时 dpl 为 0 </span></span><br><span class="line">GDT_SIZE    <span class="built_in">equ</span> $ - GDT_BASE </span><br><span class="line">GDT_LIMIT   <span class="built_in">equ</span> GDT_SIZE - <span class="number">1</span> </span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">50</span> <span class="built_in">dq</span> <span class="number">0</span> <span class="comment">; 此处预留 50 个描述符的空位(为什么不是60？因为我开头调用jmp，使得total_mem_bytes无法刚好是0xb00)  times 是 nasm 提供的伪指令，用来重复执行 times 后面表达式(编译器执行)</span></span><br><span class="line"></span><br><span class="line">SELECTOR_CODE <span class="built_in">equ</span> (<span class="number">0x0001</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 </span><br><span class="line"> <span class="comment">; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 </span></span><br><span class="line">SELECTOR_DATA <span class="built_in">equ</span> (<span class="number">0x0002</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line">SELECTOR_VIDEO <span class="built_in">equ</span> (<span class="number">0x0003</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">0x200</span> - ($ -$$) <span class="built_in">db</span> <span class="number">0</span>  <span class="comment">;填充0，使得total_mem_bytes 的节内偏移一定为0x200,地址一定为0xb00</span></span><br><span class="line">total_mem_bytes <span class="built_in">dd</span> <span class="number">0</span></span><br><span class="line"><span class="comment">; total_mem_bytes 用于保存内存容量，以字节为单位，这个位置比价好记</span></span><br><span class="line"><span class="comment">; 当前偏移loader.bin文件头0x200 字节</span></span><br><span class="line"><span class="comment">; loader.bin加载地址为 0x900</span></span><br><span class="line"><span class="comment">; 所以 total_mem_bytes 内存地址为 0xb00</span></span><br><span class="line"><span class="comment">; 将来在内核中我们会引用这个地址，同时等会验证内存容量的时候，GDB调试也可以通过读取这个地址的内容来查看大小</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;以下是 gdt 的指针，前 2 字节是 gdt 界限，后 4 字节是 gdt 起始地址</span></span><br><span class="line"><span class="symbol">gdt_ptr:</span> </span><br><span class="line">        <span class="built_in">dw</span> GDT_LIMIT </span><br><span class="line">        <span class="built_in">dd</span> GDT_BASE </span><br><span class="line">        loadermsg <span class="built_in">db</span> <span class="string">'Mosue'</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;人工对齐计算大小:total_mem_bytes(4) + gdt_ptr(6) + ards_buf(239) + ards_nr(2) + loadermsg（5） = 256字节</span></span><br><span class="line">ards_buf <span class="built_in">times</span> <span class="number">244</span> <span class="built_in">db</span> <span class="number">0</span>     <span class="comment">;填充对齐使用</span></span><br><span class="line">ards_nr <span class="built_in">dw</span> <span class="number">0</span>                <span class="comment">;用来记录ARDS结构体数量</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">loader_start:</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------0xe820 获取内存方法----------------------</span></span><br><span class="line"><span class="comment">;int 15h eax = 0000E820h , edx = 534D4150h ("SMAP") 获取内存布局</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ebx</span>,<span class="built_in">ebx</span>             <span class="comment">;第一次调用，ebx的值设置为0</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="number">0x534D4150</span>      <span class="comment">;赋值一次，以后循环的时候不会变化</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">di</span>,ards_buf         <span class="comment">;ards结构体缓冲区</span></span><br><span class="line"><span class="symbol">.e820_mem_get_loop:</span>         <span class="comment">;循环或得每个ARDS结构体</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,<span class="number">0x0000e820</span>      <span class="comment">;执行int 0x15 之后，eax的值会变成0x534D4150,所以每次执行int之前都要更新为子功能号(即0x0000e820)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">20</span>              <span class="comment">;ARDS地址范围描述符的结构大小为20字节</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                <span class="comment">;执行中断</span></span><br><span class="line">    <span class="keyword">jc</span> .e820_failed_so_try_e801     <span class="comment">;如果cf位为1，则说明有错误发生，那么尝试使用0xe801功能</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">di</span>,<span class="built_in">cx</span>               <span class="comment">;使di增加字节指向缓冲区中新的ARDS结构体的位置</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">word</span> [ards_nr]      <span class="comment">;记录ARDS数量</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">ebx</span>,<span class="number">0</span>               <span class="comment">;若ebx为0，且cf不为1，表示ards全部返回，也就是内存读取完毕了</span></span><br><span class="line">    <span class="keyword">jnz</span> .e820_mem_get_loop</span><br><span class="line"></span><br><span class="line"><span class="comment">;在所有ards结构中，找出((base_add_low + length_low)的最大值，即内存的容量</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,[ards_nr]        <span class="comment">;遍历每一个ARDS结构体，循环次数为ARDS的数量（cx寄存器）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,ards_buf</span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">edx</span>,<span class="built_in">edx</span>             <span class="comment">;edx即为最大内存容量，在此先请0</span></span><br><span class="line"><span class="symbol">.find_max_mem_area:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,[<span class="built_in">ebx</span>]           <span class="comment">;base_add_low</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>,[<span class="built_in">ebx</span>+<span class="number">8</span>]         <span class="comment">;length_low</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,<span class="number">20</span>              <span class="comment">;指向下一个ARDS结构</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>             <span class="comment">;比较当前最大值(edx)和新计算的值(eax)</span></span><br><span class="line">    <span class="keyword">jge</span> .next_ards          <span class="comment">;冒泡思想找出最大，edx寄存器始终是最大的内存容量，如果edx&gt;=eax,则跳过更新</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>             <span class="comment">;否则更新edx,edx 为总内存大小</span></span><br><span class="line"><span class="symbol">.next_ards:</span></span><br><span class="line">    <span class="keyword">loop</span> .find_max_mem_area <span class="comment">;直到(cx--)清零</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">;----------------------int 15h ax = 0xe801h 获取内存方法 最大支持4G----------------------</span></span><br><span class="line"> <span class="comment">;返回后ax，cx只一样，kb为单位，bx，dx值一样，以64kb为单位</span></span><br><span class="line"> <span class="comment">;在ax和cx寄存器中的为低15MB，bx和dx中为16MB到4GB</span></span><br><span class="line"><span class="symbol"> .e820_failed_so_try_e801:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="number">0xe801</span>               <span class="comment">;选择0xe801方法</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                    <span class="comment">;调用中断</span></span><br><span class="line">    <span class="keyword">jc</span> .e801_failed_so_try_e88  <span class="comment">;如果调用失败，尝试使用0x88方法</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;1.先计算低15MB的内存数量，并将kb转换为byte为单位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">0x400</span>            <span class="comment">;cx与ax一样，cx用作乘数</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">cx</span>                  <span class="comment">;dx:ax = 1024*ax</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">edx</span>,<span class="number">16</span>              <span class="comment">;edx &lt;&lt; 16 </span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">eax</span>,<span class="number">0x0000FFFF</span>      <span class="comment">;eax &amp;= 0x0000FFFF</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>              <span class="comment">;edx = edx | eax 得到完整的地址</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">0x100000</span>        <span class="comment">;ax只是15MB，所以要添加1MB</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="built_in">edx</span>             <span class="comment">;先把低15MB的内存容量存取esi寄存器备份</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;2.将16MB以上的内存转换为byte为单位</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">eax</span>,<span class="built_in">eax</span>             <span class="comment">;将eax的值清零</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">bx</span>               </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">0x10000</span>         <span class="comment">;0x10000:64kb</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">ecx</span>                 <span class="comment">;32位乘法，默认被乘数为eax,积为64位</span></span><br><span class="line">                            <span class="comment">;高32为存入edx,低32位存入eax,但是这个方法只能检测4GB，所以32位eax就够(edx一定为0)，所以直接相加eax即可</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esi</span>,<span class="built_in">eax</span>             <span class="comment">;将15MB以下的结果和低32位的结果相加</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="built_in">esi</span>             <span class="comment">;edx即为总内存大小</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">;----------------------int 15h ah = 0x88 获取内存方法 最大支持64MB----------------------</span></span><br><span class="line"> <span class="comment">;返回后ax,以kb为单位的内存容量，这里同样转换为byte,注意0x88子功能只会返回1MB以上的内容，所以最后要添加1MB</span></span><br><span class="line"><span class="symbol">.e801_failed_so_try_e88:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="number">ah</span>,<span class="number">0x88</span>             <span class="comment">;0x88方法</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                <span class="comment">;进入中断</span></span><br><span class="line">    <span class="keyword">jc</span> .error_hlt           <span class="comment">;如果调用失败，进入error_hlt</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">eax</span>,<span class="number">0x0000FFFF</span>      <span class="comment">;取低16位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">0x400</span>            <span class="comment">;1024</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">cx</span>                  <span class="comment">;dx:ax = cx*ax</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">edx</span> ,<span class="number">16</span>             <span class="comment">;edx &lt;&lt; 16</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>              <span class="comment">;edx = edx | eax</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">0x100000</span>        <span class="comment">;添加1MB，edx就是最终内存了</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功   其实可以不用跳转，因为下一个指令就是了</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.mem_get_ok:</span> <span class="comment">;获取内存成功</span></span><br><span class="line">    <span class="keyword">mov</span> [total_mem_bytes],<span class="built_in">edx</span>   <span class="comment">;将内存大小(byte)写入total_mem_bytes保存</span></span><br><span class="line">    <span class="keyword">jmp</span> .print_int</span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.error_hlt:</span></span><br><span class="line">    <span class="keyword">jmp</span> $</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号:0x13 功能描述:打印字符串</span></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入: </span></span><br><span class="line"><span class="comment">;AH 子功能号=13H </span></span><br><span class="line"><span class="comment">;BH = 页码</span></span><br><span class="line"><span class="comment">;BL = 属性(若 AL=00H 或 01H) </span></span><br><span class="line"><span class="comment">;CX=字符串长度</span></span><br><span class="line"><span class="comment">;(DH､ DL)=坐标(行 列､ ) </span></span><br><span class="line"><span class="comment">;ES:BP=字符串地址</span></span><br><span class="line"><span class="comment">;AL=显示输出方式</span></span><br><span class="line"><span class="comment">; 0—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 1—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置改变</span></span><br><span class="line"><span class="comment">; 2—字符串中含显示字符和显示属性。显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 3—字符串中含显示字符和显示属性。显示后，光标位置改变</span></span><br><span class="line"><span class="comment">;无返回值</span></span><br><span class="line"><span class="symbol">.print_int:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">sp</span>, LOADER_BASE_ADDR </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bp</span>, loadermsg   <span class="comment">; ES:BP = 字符串地址</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">5</span>          <span class="comment">; CX = 字符串长度</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0x1301</span>      <span class="comment">; AH = 13, AL = 01h </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0x001f</span>      <span class="comment">; 页号为 0(BH = 0) 蓝底粉红字(BL = 1fh) </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1800</span> </span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x10</span>            <span class="comment">; 10h 号中断</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;-------------------- 准备进入保护模式 ------------------------------- </span></span><br><span class="line">    <span class="comment">;1 打开 A20 </span></span><br><span class="line">    <span class="comment">;2 加载 gdt </span></span><br><span class="line">    <span class="comment">;3 将 cr0 的 pe 位置 1 </span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 1. 打开 A20 ---------------- </span></span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">al</span>,<span class="number">0x92</span> </span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">al</span>,<span class="number">0000_0010B</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="number">0x92</span>,<span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 2. 加载 GDT ---------------- </span></span><br><span class="line">    <span class="keyword">lgdt</span> [gdt_ptr] </span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 3. cr0 第 0 位置 1 ---------------- </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="built_in">cr0</span> </span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, <span class="number">0x00000001</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cr0</span>, <span class="built_in">eax</span> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> <span class="built_in">dword</span> SELECTOR_CODE:p_mode_start <span class="comment">; 刷新流水线</span></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>] </span><br><span class="line"><span class="symbol">p_mode_start:</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_DATA </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ds</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">es</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ss</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esp</span>,LOADER_STACK_TOP </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_VIDEO </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">gs</span>, <span class="built_in">ax</span> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">160</span>], <span class="string">'P'</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">161</span>], <span class="number">0x07</span>   <span class="comment">; 灰底黑字</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">162</span>], <span class="string">'2'</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">163</span>], <span class="number">0x07</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">164</span>], <span class="string">'L'</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">165</span>], <span class="number">0x07</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> $</span><br></pre></td></tr></table></figure></div><p>然后又是熟练的编译，写入，运行，GDB调试验证结果</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">nasm -I include/ -o loader.bin loader.S</span><br><span class="line"><span class="comment"># 注意这里count改成了3,，因为这个bin文件大小应该有个1070bytes</span></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/home/mouse/OS_mouse/tool/bochs/mouse/loader.bin of=/home/mouse/OS_mouse/tool/bochs/hd60M.img bs=512 count=3 seek=2 conv=notrunc</span><br><span class="line"><span class="comment">#跳过运行的指令</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#------打开GDB调试--------------</span></span><br><span class="line">(gdb) x/xw 0xb00</span><br><span class="line"><span class="comment">#然后如果代码和配置文件都对的话，结果应该是0xb00:  0x02000000</span></span><br></pre></td></tr></table></figure></div><p>这里的<code>x/xw</code>就是GDB的功能(以十六进制查看 0xb00处的 4 字节),得到的结果<code>0x2000000</code>转化为MB就是32MB，大家可以自己计算一下</p><hr><h3 id="G-2-内存分页，启动"><a href="#G-2-内存分页，启动" class="headerlink" title="G.2 内存分页，启动"></a>G.2 内存分页，启动</h3><blockquote><p>在此之前，我们的程序虽然已经进了保护模式，地址空间到达了前所未有的 4GB，但其依然是受限制<br>的，就像共享网络带宽一样，此进程要和其他进程包括操作系统共享这 4GB 内存空间。我们把段基址+<br>段内偏移地址称为线性地址，线性地址是唯一的，只能属于某一个进程</p></blockquote><hr><h4 id="G2-1-内存分页机制"><a href="#G2-1-内存分页机制" class="headerlink" title="G2.1 内存分页机制"></a>G2.1 内存分页机制</h4><blockquote><p>一直以来我们都直接在内存分段机制下工作，目前未出问题看似良好，的确，目前咱们的应用过于简<br>单了，就一个 loader 在跑，能出什么问题呢？可是想象一下，当我们物理内存不足时会怎么办呢？比如系<br>统里的应用程序过多，或者内存碎片过多无法容纳新的进程，或者曾经被换出到硬盘中的内存段需要再次<br>重新装到内存，可是内存中找不到合适大小的内存区域怎么办</p></blockquote><p>比较形象的例子就是:假设有三个进程A,B,C在运行，显然，我们会将他们三按顺序排好放在内存中，然后当B进程结束了，这个时候B的内存就释放了，可以这个时候如果来了一个进程D，它占用的内存大于进程B，也大于剩余的内存(总内存-进程A-进程B-进程C),但是小于内存中空闲的内存，那么实际上内存是够的，但是这个进程却是安排不进去的<br>解决办法有两种:</p><ol><li>等待C进程结束，然后D进程再运行，但是<strong>这样会很浪费时间</strong>，因为谁也不知道C进程要运行多久</li><li>将A的进程/C的进程部分换到硬盘上，腾出可以容纳D的内存，可是<strong>有些硬盘的速度也很慢</strong></li></ol><hr><ul><li><strong>一级页表</strong></li></ul><p>首先分页要建立在之前说过的分段上面，经过段部件处理后，保护模式的寻址空间是 4GB(指线性地址空间),分页机制的思想是:<strong>通过映射，可以使连续的线性地址与任意物理内存地址相关联，逻辑上连续的线性地址其对应的物理地址可以不连续</strong></p><p>分页机制:<br><strong>1.即可以将线性地址转换为物理地址</strong><br><strong>2.也可以用大小相等的页代表大小不等的段</strong></p><p>通过线性地址到真实物理地址的映射，经过段部件输出的线性地址便有了另一个名字: <strong>虚拟地址</strong><br>在页表中保存着线性地址和物理地址的映射关系，页表中的每一行被称为页表项(4字节)</p><blockquote><p>一个页表项对应一个页，所以，用线性地址的高 20 位作为页表项的索引，每个页表项要占用 4 字节<br>大小，所以这高 20 位的索引乘以 4 后才是该页表项相对于页表物理地址的字节偏移量。用 cr3 寄存器中<br>的页表物理地址加上此偏移量便是该页表项的物理地址，从该页表项中得到映射的物理页地址，然后用线<br>性地址的低 12 位与该物理页地址相加，所得的地址之和便是最终要访问的物理地址。</p></blockquote><p>cpu中集成好的<strong>页部件</strong>用来专门计算这个</p><ul><li><strong>二级页表</strong></li></ul><p>一级也页表目前的缺点有:</p><ol><li>一级页表中最多可容纳（1M）个页表项，每个页表项为4字节，则是4MB</li><li>一级页表中的所有页表项必须提前建立好，因为操作系统要占用4GB中的高1GB，然后用户占3GB</li><li>每个进程都有自己的页表，进程一多，那么占用的空间也会很多</li></ol><p>总结一下需要解决的问题是:<strong>不要一次性地将全部页表项建好，需要时动态创建页表项</strong></p><p>专门有个<strong>页目录</strong>表来存储这些页表，每个页表的物理地址在页目录表中都以<strong>页目录项</strong>的形式存储（与表项一样），</p><p>具体大家可以看书中的讲解/网上查询，更加详细</p><ul><li><strong>启动分页机制</strong></li></ul><p>启动分页机制的开关是将控制寄存器 cr0 的 PG 位置 1，PG 位是 cr0 寄存器的最后一位：第31位</p><blockquote><p>PG 位为 1 后便进入了内存分页运行机制，段部件输出的线性地址成为虚拟地址（顺便说一下，第 0 位是 PE<br>位，用来进入保护模式的开关）。在将 PG 位置 1 之前，系统都是在内存分段机制下工作，段部件输出的线<br>性地址便直接是物理地址，也就意味着在第 2 步中，cr3 寄存器中的页表地址是真实的物理地址</p></blockquote><hr><h4 id="G2-2-规划页表之操作系统与用户进程的关系"><a href="#G2-2-规划页表之操作系统与用户进程的关系" class="headerlink" title="G2.2 规划页表之操作系统与用户进程的关系"></a>G2.2 规划页表之操作系统与用户进程的关系</h4><p>分页的第一步是要<strong>准备好一个页表</strong></p><blockquote><p>为了计算机安全，用户进程必须运行在低特权级，当用户进程需要访问硬件相关的资源时，需要向操作系统申请，由操作系统去做，之后将结果返回给用户进程。进程可以有无限多个，而操作系统只有一个，所以，操作系统必须“共享”给所有用户进程<br>页表的设计是要根据内存分布情况来决定的，我们也学习 Linux 的作法，在用户进程 4GB 虚拟地址空间的高 3GB 以上的部分划分给操作系统，0～3GB 是用户进程自己的虚拟空间。为了实现共享操作系统，让所有用户进程 3GB～4GB 的虚拟地址空间都指向同一个操作系统，也就是所有进程的虚拟地址 3GB～4GB 本质上都是指向的同一片物理页地址，这片物理页上是操作系统的实体代码</p></blockquote><p>页目录表的位置，我们就放在物理地址<code>0x100000</code> 处(非必须，我就跟着作者来吧)，咱们让页表紧挨着页目录表。页目录本身占 4KB，所以第一个页表的物理地址是 <code>0x101000</code>，下面就具体看看如何创建一个页目录和页表(这只是后面整体代码中的一个函数)，同样也是启用内存分页进制三部曲之一</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">;---------------------创建页目录和页表-------------------</span></span><br><span class="line"><span class="comment">; 页目录占用4kb，也就是4096字节(0x1000),PAGE_DIR_TABLE_POS是页目录的物理地址(0x100000)</span></span><br><span class="line"><span class="comment">;循环清零</span></span><br><span class="line"><span class="symbol">setup_page:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">4096</span>    </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="number">0</span></span><br><span class="line"><span class="symbol">.clear_page_dir:</span>    <span class="comment">;清理页目录</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [PAGE_DIR_TABLE_POS + <span class="built_in">esi</span>],<span class="number">0</span>   <span class="comment">;清零</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span>                                 <span class="comment">;偏移一个字节,计数+1</span></span><br><span class="line">    <span class="keyword">loop</span> .clear_page_dir                    <span class="comment">;不断循环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;开始创建页目录(PDE)</span></span><br><span class="line"><span class="symbol">.creat_pde:</span>              <span class="comment">; 创建 Page Directory Entry</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>,<span class="number">0x1000</span>      <span class="comment">;此时的eax为第一个页表的位置和属性(偏移4kb)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,<span class="built_in">eax</span>         <span class="comment">;此处为ebx复制，是为了.creat_pte做准备，ebx为基地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 下面将页目录项 0 和 0xc00 都存为第一个页表的地址，每个页表表示 4MB 内存</span></span><br><span class="line"><span class="comment">; 这样 0xc03fffff 以下的地址和 0x003fffff 以下的地址都指向相同的页表</span></span><br><span class="line"><span class="comment">; 这是为将地址映射为内核地址做准备</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>,PG_US_U | PG_RW_W | PG_P         <span class="comment">;页目录项的属性 RW 和 P 位为 1，US 为 1，表示用户属性，所有特权级别都可以访问</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS + <span class="number">0X0</span>],<span class="built_in">eax</span>      <span class="comment">;第一个目录项，在页目录表中的第 1 个目录项写入第一个页表的位置(0x101000)及属性(7)</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS + <span class="number">0xc00</span>], <span class="built_in">eax</span>   <span class="comment">;0XC00是第768个页表占用的目录项(一个页表项占用4字节)，0xc00以上的目录项用于内核空间</span></span><br><span class="line"><span class="comment">; 也就是说页表的 0xc0000000～0xffffffff 共计 1G 属于内核</span></span><br><span class="line"><span class="comment">;               0x0～0xbfffffff 共计 3G 属于用户进程</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="built_in">eax</span>,<span class="number">0x1000</span>                      <span class="comment">;计算目录表自己的物理地址</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS+<span class="number">4092</span>],<span class="built_in">eax</span>   <span class="comment">;使最后一个目录项指向页目录表自己的地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;创建页表项(PTE)        </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">256</span>         <span class="comment">;1M 低端内存 / 每页大小 4k =256</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, <span class="number">0</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,PG_US_U | PG_RW_W |PG_P     <span class="comment">;属性为7，US=1,RW=1,P=1</span></span><br><span class="line"><span class="symbol">.creat_pte:</span>              <span class="comment">; 创建 Page Table Entry</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">esi</span>*<span class="number">4</span>],<span class="built_in">edx</span> <span class="comment">; 此时的edx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">4096</span>        <span class="comment">; 4kb</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span>             <span class="comment">; 计数+1</span></span><br><span class="line">    <span class="keyword">loop</span> .creat_pte     <span class="comment">; 循环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;创建内核其它页表的PDE</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, <span class="number">0x2000</span>                     <span class="comment">;此时eax为第二个页表的位置，偏移8kb</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, PG_US_U | PG_RW_W | PG_P    <span class="comment">;页目录项的属性 US､ RW 和 P 位都为 1</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">254</span>                        <span class="comment">;范围为第 769～1022 的所有目录项数量</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, <span class="number">769</span></span><br><span class="line"><span class="symbol">.create_kernel_pde:</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">esi</span>*<span class="number">4</span>],<span class="built_in">eax</span>                 </span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, <span class="number">0x1000</span></span><br><span class="line">    <span class="keyword">loop</span> .create_kernel_pde</span><br><span class="line">    <span class="keyword">ret</span></span><br></pre></td></tr></table></figure></div><h4 id="G2-3-完整页表操作代码"><a href="#G2-3-完整页表操作代码" class="headerlink" title="G2.3 完整页表操作代码"></a>G2.3 完整页表操作代码</h4><p>下面就是整体loader.S的代码，成功运行后可以通过显存(<strong>GDT 的基址会变成 3GB 之上的虚拟地址，显存段基址也变成了 3GB 这上的虚拟地址</strong>)显示一个”V”<br>同时，我们的boot.inc文件也要稍微修改一下，添加点新的描述:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">;/home/mouse/OS_mouse/tool/bochs/mouse/boot.inc</span><br><span class="line">;.....略</span><br><span class="line">;--------------------- loader &amp; kernel -------------------------</span><br><span class="line">PAGE_DIR_TABLE_POS equ <span class="number">0x100000</span>     ;页表的基地址</span><br><span class="line">;.....略</span><br><span class="line">;----------------------------------页表相关属性-----------------------</span><br><span class="line">PG_P equ <span class="number">1b</span> </span><br><span class="line">PG_RW_R equ <span class="number">00b</span> </span><br><span class="line">PG_RW_W equ <span class="number">10b</span> </span><br><span class="line">PG_US_S equ <span class="number">000b</span> </span><br><span class="line">PG_US_U equ <span class="number">100b</span></span><br><span class="line">;.....略</span><br></pre></td></tr></table></figure></div><p>然后就是添加页表的初始化，和新的GDT以及显存了（我这里保留了一些之前的打印代码）</p><div class="code-container" data-rel="X86asm"><figure class="iseeu highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; /home/mouse/OS_mouse/tool/bochs/mouse/loader.S</span></span><br><span class="line"></span><br><span class="line"><span class="meta">%include</span> <span class="string">"boot.inc"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">section</span> loader vstart=LOADER_BASE_ADDR</span><br><span class="line">LOADER_STACK_TOP <span class="built_in">equ</span> LOADER_BASE_ADDR</span><br><span class="line"><span class="keyword">jmp</span> loader_start</span><br><span class="line"></span><br><span class="line"><span class="comment">; 构建gdt及其内部的描述符,拆分为上4字节，下4字节</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">GDT_BASE:</span>   <span class="built_in">dd</span> <span class="number">0x00000000</span>       <span class="comment">;空描述段</span></span><br><span class="line">            <span class="built_in">dd</span> <span class="number">0x00000000</span>       </span><br><span class="line"><span class="symbol">CODE_DESC:</span>  <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">            <span class="built_in">dd</span> DESC_CODE_HIGH4    </span><br><span class="line"><span class="symbol">DATA_STACK_DESC:</span>    <span class="built_in">dd</span> <span class="number">0x0000FFFF</span> </span><br><span class="line">                    <span class="built_in">dd</span> DESC_DATA_HIGH4 </span><br><span class="line"><span class="symbol">VIDEO_DESC:</span> <span class="built_in">dd</span> <span class="number">0x80000007</span>               <span class="comment">;limit=(0xbffff-0xb8000)/4k=0x7 </span></span><br><span class="line">            <span class="built_in">dd</span> DESC_VIDEO_HIGH4         <span class="comment">;此时 dpl 为 0 </span></span><br><span class="line">GDT_SIZE    <span class="built_in">equ</span> $ - GDT_BASE </span><br><span class="line">GDT_LIMIT   <span class="built_in">equ</span> GDT_SIZE - <span class="number">1</span> </span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">50</span> <span class="built_in">dq</span> <span class="number">0</span> <span class="comment">; 此处预留 50 个描述符的空位(为什么不是60？因为我开头调用jmp，使得total_mem_bytes无法刚好是0xb00)  times 是 nasm 提供的伪指令，用来重复执行 times 后面表达式(编译器执行)</span></span><br><span class="line"></span><br><span class="line">SELECTOR_CODE <span class="built_in">equ</span> (<span class="number">0x0001</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 </span><br><span class="line"> <span class="comment">; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 </span></span><br><span class="line">SELECTOR_DATA <span class="built_in">equ</span> (<span class="number">0x0002</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line">SELECTOR_VIDEO <span class="built_in">equ</span> (<span class="number">0x0003</span>&lt;&lt;<span class="number">3</span>) + TI_GDT + RPL0 <span class="comment">; 同上</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">times</span> <span class="number">0x200</span> - ($ -$$) <span class="built_in">db</span> <span class="number">0</span>  <span class="comment">;填充0，使得total_mem_bytes 的节内偏移一定为0x200,地址一定为0xb00</span></span><br><span class="line">total_mem_bytes <span class="built_in">dd</span> <span class="number">0</span></span><br><span class="line"><span class="comment">; total_mem_bytes 用于保存内存容量，以字节为单位，这个位置比价好记</span></span><br><span class="line"><span class="comment">; 当前偏移loader.bin文件头0x200 字节</span></span><br><span class="line"><span class="comment">; loader.bin加载地址为 0x900</span></span><br><span class="line"><span class="comment">; 所以 total_mem_bytes 内存地址为 0xb00</span></span><br><span class="line"><span class="comment">; 将来在内核中我们会引用这个地址，同时等会验证内存容量的时候，GDB调试也可以通过读取这个地址的内容来查看大小</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;以下是 gdt 的指针，前 2 字节是 gdt 界限，后 4 字节是 gdt 起始地址</span></span><br><span class="line"><span class="symbol">gdt_ptr:</span> </span><br><span class="line">        <span class="built_in">dw</span> GDT_LIMIT </span><br><span class="line">        <span class="built_in">dd</span> GDT_BASE </span><br><span class="line">        loadermsg <span class="built_in">db</span> <span class="string">'Mosue'</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;人工对齐计算大小:total_mem_bytes(4) + gdt_ptr(6) + ards_buf(239) + ards_nr(2) + loadermsg（5） = 256字节</span></span><br><span class="line">ards_buf <span class="built_in">times</span> <span class="number">244</span> <span class="built_in">db</span> <span class="number">0</span>     <span class="comment">;填充对齐使用</span></span><br><span class="line">ards_nr <span class="built_in">dw</span> <span class="number">0</span>                <span class="comment">;用来记录ARDS结构体数量</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">loader_start:</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">;----------------------0xe820 获取内存方法----------------------</span></span><br><span class="line"><span class="comment">;int 15h eax = 0000E820h , edx = 534D4150h ("SMAP") 获取内存布局</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">ebx</span>,<span class="built_in">ebx</span>             <span class="comment">;第一次调用，ebx的值设置为0</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="number">0x534D4150</span>      <span class="comment">;赋值一次，以后循环的时候不会变化</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">di</span>,ards_buf         <span class="comment">;ards结构体缓冲区</span></span><br><span class="line"><span class="symbol">.e820_mem_get_loop:</span>         <span class="comment">;循环或得每个ARDS结构体</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,<span class="number">0x0000e820</span>      <span class="comment">;执行int 0x15 之后，eax的值会变成0x534D4150,所以每次执行int之前都要更新为子功能号(即0x0000e820)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">20</span>              <span class="comment">;ARDS地址范围描述符的结构大小为20字节</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                <span class="comment">;执行中断</span></span><br><span class="line">    <span class="keyword">jc</span> .e820_failed_so_try_e801     <span class="comment">;如果cf位为1，则说明有错误发生，那么尝试使用0xe801功能</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">di</span>,<span class="built_in">cx</span>               <span class="comment">;使di增加字节指向缓冲区中新的ARDS结构体的位置</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">word</span> [ards_nr]      <span class="comment">;记录ARDS数量</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">ebx</span>,<span class="number">0</span>               <span class="comment">;若ebx为0，且cf不为1，表示ards全部返回，也就是内存读取完毕了</span></span><br><span class="line">    <span class="keyword">jnz</span> .e820_mem_get_loop</span><br><span class="line"></span><br><span class="line"><span class="comment">;在所有ards结构中，找出((base_add_low + length_low)的最大值，即内存的容量</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,[ards_nr]        <span class="comment">;遍历每一个ARDS结构体，循环次数为ARDS的数量（cx寄存器）</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,ards_buf</span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">edx</span>,<span class="built_in">edx</span>             <span class="comment">;edx即为最大内存容量，在此先请0</span></span><br><span class="line"><span class="symbol">.find_max_mem_area:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,[<span class="built_in">ebx</span>]           <span class="comment">;base_add_low</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>,[<span class="built_in">ebx</span>+<span class="number">8</span>]         <span class="comment">;length_low</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">ebx</span>,<span class="number">20</span>              <span class="comment">;指向下一个ARDS结构</span></span><br><span class="line">    <span class="keyword">cmp</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>             <span class="comment">;比较当前最大值(edx)和新计算的值(eax)</span></span><br><span class="line">    <span class="keyword">jge</span> .next_ards          <span class="comment">;冒泡思想找出最大，edx寄存器始终是最大的内存容量，如果edx&gt;=eax,则跳过更新</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>             <span class="comment">;否则更新edx,edx 为总内存大小</span></span><br><span class="line"><span class="symbol">.next_ards:</span></span><br><span class="line">    <span class="keyword">loop</span> .find_max_mem_area <span class="comment">;直到(cx--)清零</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">;----------------------int 15h ax = 0xe801h 获取内存方法 最大支持4G----------------------</span></span><br><span class="line"> <span class="comment">;返回后ax，cx只一样，kb为单位，bx，dx值一样，以64kb为单位</span></span><br><span class="line"> <span class="comment">;在ax和cx寄存器中的为低15MB，bx和dx中为16MB到4GB</span></span><br><span class="line"><span class="symbol"> .e820_failed_so_try_e801:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="number">0xe801</span>               <span class="comment">;选择0xe801方法</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                    <span class="comment">;调用中断</span></span><br><span class="line">    <span class="keyword">jc</span> .e801_failed_so_try_e88  <span class="comment">;如果调用失败，尝试使用0x88方法</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;1.先计算低15MB的内存数量，并将kb转换为byte为单位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">0x400</span>            <span class="comment">;cx与ax一样，cx用作乘数</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">cx</span>                  <span class="comment">;dx:ax = 1024*ax</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">edx</span>,<span class="number">16</span>              <span class="comment">;edx &lt;&lt; 16 </span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">eax</span>,<span class="number">0x0000FFFF</span>      <span class="comment">;eax &amp;= 0x0000FFFF</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>              <span class="comment">;edx = edx | eax 得到完整的地址</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">0x100000</span>        <span class="comment">;ax只是15MB，所以要添加1MB</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="built_in">edx</span>             <span class="comment">;先把低15MB的内存容量存取esi寄存器备份</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;2.将16MB以上的内存转换为byte为单位</span></span><br><span class="line">    <span class="keyword">xor</span> <span class="built_in">eax</span>,<span class="built_in">eax</span>             <span class="comment">;将eax的值清零</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>,<span class="built_in">bx</span>               </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">0x10000</span>         <span class="comment">;0x10000:64kb</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">ecx</span>                 <span class="comment">;32位乘法，默认被乘数为eax,积为64位</span></span><br><span class="line">                            <span class="comment">;高32为存入edx,低32位存入eax,但是这个方法只能检测4GB，所以32位eax就够(edx一定为0)，所以直接相加eax即可</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esi</span>,<span class="built_in">eax</span>             <span class="comment">;将15MB以下的结果和低32位的结果相加</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,<span class="built_in">esi</span>             <span class="comment">;edx即为总内存大小</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">;----------------------int 15h ah = 0x88 获取内存方法 最大支持64MB----------------------</span></span><br><span class="line"> <span class="comment">;返回后ax,以kb为单位的内存容量，这里同样转换为byte,注意0x88子功能只会返回1MB以上的内容，所以最后要添加1MB</span></span><br><span class="line"><span class="symbol">.e801_failed_so_try_e88:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="number">ah</span>,<span class="number">0x88</span>             <span class="comment">;0x88方法</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x15</span>                <span class="comment">;进入中断</span></span><br><span class="line">    <span class="keyword">jc</span> .error_hlt           <span class="comment">;如果调用失败，进入error_hlt</span></span><br><span class="line">    <span class="keyword">and</span> <span class="built_in">eax</span>,<span class="number">0x0000FFFF</span>      <span class="comment">;取低16位</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>,<span class="number">0x400</span>            <span class="comment">;1024</span></span><br><span class="line">    <span class="keyword">mul</span> <span class="built_in">cx</span>                  <span class="comment">;dx:ax = cx*ax</span></span><br><span class="line">    <span class="keyword">shl</span> <span class="built_in">edx</span> ,<span class="number">16</span>             <span class="comment">;edx &lt;&lt; 16</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">edx</span>,<span class="built_in">eax</span>              <span class="comment">;edx = edx | eax</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">0x100000</span>        <span class="comment">;添加1MB，edx就是最终内存了</span></span><br><span class="line">    <span class="keyword">jmp</span> .mem_get_ok         <span class="comment">;获取内存成功   其实可以不用跳转，因为下一个指令就是了</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.mem_get_ok:</span> <span class="comment">;获取内存成功</span></span><br><span class="line">    <span class="keyword">mov</span> [total_mem_bytes],<span class="built_in">edx</span>   <span class="comment">;将内存大小(byte)写入total_mem_bytes保存</span></span><br><span class="line">    <span class="keyword">jmp</span> .print_int              <span class="comment">;打印个mouse</span></span><br><span class="line"><span class="symbol"></span></span><br><span class="line"><span class="symbol">.error_hlt:</span>                     <span class="comment">;失败情况下</span></span><br><span class="line">    <span class="keyword">jmp</span> $</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;INT 0x10 功能号:0x13 功能描述:打印字符串</span></span><br><span class="line"><span class="comment">;------------------------------------------------------------ </span></span><br><span class="line"><span class="comment">;输入: </span></span><br><span class="line"><span class="comment">;AH 子功能号=13H </span></span><br><span class="line"><span class="comment">;BH = 页码</span></span><br><span class="line"><span class="comment">;BL = 属性(若 AL=00H 或 01H) </span></span><br><span class="line"><span class="comment">;CX=字符串长度</span></span><br><span class="line"><span class="comment">;(DH､ DL)=坐标(行 列､ ) </span></span><br><span class="line"><span class="comment">;ES:BP=字符串地址</span></span><br><span class="line"><span class="comment">;AL=显示输出方式</span></span><br><span class="line"><span class="comment">; 0—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 1—字符串中只含显示字符，其显示属性在 BL 中</span></span><br><span class="line"><span class="comment">;显示后，光标位置改变</span></span><br><span class="line"><span class="comment">; 2—字符串中含显示字符和显示属性。显示后，光标位置不变</span></span><br><span class="line"><span class="comment">; 3—字符串中含显示字符和显示属性。显示后，光标位置改变</span></span><br><span class="line"><span class="comment">;无返回值</span></span><br><span class="line"><span class="symbol">.print_int:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">sp</span>, LOADER_BASE_ADDR </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bp</span>, loadermsg   <span class="comment">; ES:BP = 字符串地址</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cx</span>, <span class="number">5</span>          <span class="comment">; CX = 字符串长度</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, <span class="number">0x1301</span>      <span class="comment">; AH = 13, AL = 01h </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">bx</span>, <span class="number">0x001f</span>      <span class="comment">; 页号为 0(BH = 0) 蓝底粉红字(BL = 1fh) </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">dx</span>, <span class="number">0x1800</span> </span><br><span class="line">    <span class="keyword">int</span> <span class="number">0x10</span>            <span class="comment">; 10h 号中断</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;-------------------- 准备进入保护模式 ------------------------------- </span></span><br><span class="line">    <span class="comment">;1 打开 A20 </span></span><br><span class="line">    <span class="comment">;2 加载 gdt </span></span><br><span class="line">    <span class="comment">;3 将 cr0 的 pe 位置 1 </span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 1. 打开 A20 ---------------- </span></span><br><span class="line">    <span class="keyword">in</span> <span class="built_in">al</span>,<span class="number">0x92</span> </span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">al</span>,<span class="number">0000_0010B</span> </span><br><span class="line">    <span class="keyword">out</span> <span class="number">0x92</span>,<span class="built_in">al</span> </span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 2. 加载 GDT ---------------- </span></span><br><span class="line">    <span class="keyword">lgdt</span> [gdt_ptr] </span><br><span class="line"></span><br><span class="line">    <span class="comment">;----------------- 3. cr0 第 0 位置 1 ---------------- </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="built_in">cr0</span> </span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, <span class="number">0x00000001</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cr0</span>, <span class="built_in">eax</span> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">jmp</span> <span class="built_in">dword</span> SELECTOR_CODE:p_mode_start <span class="comment">; 刷新流水线</span></span><br><span class="line"></span><br><span class="line">[<span class="meta">bits</span> <span class="number">32</span>] </span><br><span class="line"><span class="symbol">p_mode_start:</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_DATA </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ds</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">es</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ss</span>, <span class="built_in">ax</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esp</span>,LOADER_STACK_TOP </span><br><span class="line"></span><br><span class="line">    <span class="keyword">call</span> setup_page     <span class="comment">; 创建页目录及页表并初始化页内存位图</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;要将描述符表地址及偏移量写入内存 gdt_ptr，一会儿用新地址重新加载</span></span><br><span class="line">    <span class="keyword">sgdt</span> [gdt_ptr]      <span class="comment">; 存储到原来gdt所有的位置</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;将gdt描述符中视频段描述符中的段基址+0xc0000000</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,[gdt_ptr +<span class="number">2</span>]</span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">dword</span> [<span class="built_in">ebx</span> + <span class="number">0x18</span> +<span class="number">4</span>],<span class="number">0xc0000000</span></span><br><span class="line">    <span class="comment">;视频段时第3个段描述符，每个描述符是8字节，即0x18</span></span><br><span class="line">    <span class="comment">;段描述符的高 4 字节的最高位是段基址的第 31～24 位</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;将 gdt 的基址加上 0xc0000000 使其成为内核所在的高地址</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">dword</span> [gdt_ptr + <span class="number">2</span>],<span class="number">0xc0000000</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">esp</span>,<span class="number">0xc0000000</span>                  <span class="comment">;将栈指针同样映射到内核地址</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, PAGE_DIR_TABLE_POS         <span class="comment">;把页目录地址赋给 cr3 </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cr3</span>, <span class="built_in">eax</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="built_in">cr0</span>                        <span class="comment">;打开 cr0 的 pg 位（第 31 位）</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, <span class="number">0x80000000</span> </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">cr0</span>, <span class="built_in">eax</span> </span><br><span class="line">    </span><br><span class="line">    <span class="comment">;在开启分页后，用 gdt 新的地址重新加载</span></span><br><span class="line">    <span class="keyword">lgdt</span> [gdt_ptr]              <span class="comment">;重新加载</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">;初始化gs寄存器</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ax</span>, SELECTOR_VIDEO      <span class="comment">;这里要初始化gs寄存器，其实我尝试将它放在之前的位置初始化也可以打印成功好像</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">gs</span>, <span class="built_in">ax</span> </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [<span class="built_in">gs</span>:<span class="number">160</span>], <span class="string">'V'</span>      <span class="comment">;视频段段基址已经被更新，用字符 v 表示 virtual addr </span></span><br><span class="line">    <span class="keyword">jmp</span> $</span><br><span class="line"></span><br><span class="line"><span class="comment">;---------------------创建页目录和页表-------------------</span></span><br><span class="line"><span class="comment">; 页目录占用4kb，也就是4096字节(0x1000),PAGE_DIR_TABLE_POS是页目录的物理地址(0x100000)</span></span><br><span class="line"><span class="comment">;循环清零</span></span><br><span class="line"><span class="symbol">setup_page:</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">4096</span>    </span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>,<span class="number">0</span></span><br><span class="line"><span class="symbol">.clear_page_dir:</span>    <span class="comment">;清理页目录</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">byte</span> [PAGE_DIR_TABLE_POS + <span class="built_in">esi</span>],<span class="number">0</span>   <span class="comment">;清零</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span>                                 <span class="comment">;偏移一个字节,计数+1</span></span><br><span class="line">    <span class="keyword">loop</span> .clear_page_dir                    <span class="comment">;不断循环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;开始创建页目录(PDE)</span></span><br><span class="line"><span class="symbol">.creat_pde:</span>              <span class="comment">; 创建 Page Directory Entry</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>,PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>,<span class="number">0x1000</span>      <span class="comment">;此时的eax为第一个页表的位置和属性(偏移4kb)</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>,<span class="built_in">eax</span>         <span class="comment">;此处为ebx复制，是为了.creat_pte做准备，ebx为基地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 下面将页目录项 0 和 0xc00 都存为第一个页表的地址，每个页表表示 4MB 内存</span></span><br><span class="line"><span class="comment">; 这样 0xc03fffff 以下的地址和 0x003fffff 以下的地址都指向相同的页表</span></span><br><span class="line"><span class="comment">; 这是为将地址映射为内核地址做准备</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>,PG_US_U | PG_RW_W | PG_P         <span class="comment">;页目录项的属性 RW 和 P 位为 1，US 为 1，表示用户属性，所有特权级别都可以访问</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS + <span class="number">0X0</span>],<span class="built_in">eax</span>      <span class="comment">;第一个目录项，在页目录表中的第 1 个目录项写入第一个页表的位置(0x101000)及属性(7)</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS + <span class="number">0xc00</span>], <span class="built_in">eax</span>   <span class="comment">;0XC00是第768个页表占用的目录项(一个页表项占用4字节)，0xc00以上的目录项用于内核空间</span></span><br><span class="line"><span class="comment">; 也就是说页表的 0xc0000000～0xffffffff 共计 1G 属于内核</span></span><br><span class="line"><span class="comment">;               0x0～0xbfffffff 共计 3G 属于用户进程</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="built_in">eax</span>,<span class="number">0x1000</span>                      <span class="comment">;计算目录表自己的物理地址</span></span><br><span class="line">    <span class="keyword">mov</span> [PAGE_DIR_TABLE_POS+<span class="number">4092</span>],<span class="built_in">eax</span>   <span class="comment">;使最后一个目录项指向页目录表自己的地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;创建页表项(PTE)        </span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>,<span class="number">256</span>         <span class="comment">;1M 低端内存 / 每页大小 4k =256</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, <span class="number">0</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">edx</span>,PG_US_U | PG_RW_W |PG_P     <span class="comment">;属性为7，US=1,RW=1,P=1</span></span><br><span class="line"><span class="symbol">.creat_pte:</span>              <span class="comment">; 创建 Page Table Entry</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">esi</span>*<span class="number">4</span>],<span class="built_in">edx</span> <span class="comment">; 此时的edx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">edx</span>,<span class="number">4096</span>        <span class="comment">; 4kb</span></span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span>             <span class="comment">; 计数+1</span></span><br><span class="line">    <span class="keyword">loop</span> .creat_pte     <span class="comment">; 循环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;创建内核其它页表的PDE</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">eax</span>, PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, <span class="number">0x2000</span>                     <span class="comment">;此时eax为第二个页表的位置，偏移8kb</span></span><br><span class="line">    <span class="keyword">or</span> <span class="built_in">eax</span>, PG_US_U | PG_RW_W | PG_P    <span class="comment">;页目录项的属性 US､ RW 和 P 位都为 1</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ebx</span>, PAGE_DIR_TABLE_POS</span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">ecx</span>, <span class="number">254</span>                        <span class="comment">;范围为第 769～1022 的所有目录项数量</span></span><br><span class="line">    <span class="keyword">mov</span> <span class="built_in">esi</span>, <span class="number">769</span></span><br><span class="line"><span class="symbol">.create_kernel_pde:</span></span><br><span class="line">    <span class="keyword">mov</span> [<span class="built_in">ebx</span>+<span class="built_in">esi</span>*<span class="number">4</span>],<span class="built_in">eax</span>                 </span><br><span class="line">    <span class="keyword">inc</span> <span class="built_in">esi</span></span><br><span class="line">    <span class="keyword">add</span> <span class="built_in">eax</span>, <span class="number">0x1000</span></span><br><span class="line">    <span class="keyword">loop</span> .create_kernel_pde</span><br><span class="line">    <span class="keyword">ret</span></span><br></pre></td></tr></table></figure></div><p>突然发现GDB调试不能访问像页表，GDT这些系统的寄存器，需要通过 ​Bochs 的远程调试协议​获取，所以反而Bochs自带的调试时可以使用<code>info</code>指令快速获取，所以后面调试选择先试用Bochs自带的调试器吧（方法可以参考书籍）</p><p>这里可以通过指令查看到GDT，以及显存段描述符的段基址，已经是新的虚拟地址了</p><p>编译，写入然后运行，在指令框中输入bochs的指令:info</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&lt;bochs:3&gt; info gdt  <span class="comment">#查看gdt相关内容</span></span><br><span class="line">Global Descriptor Table (base=0xc0000903, <span class="built_in">limit</span>=31):    <span class="comment">#这里的0xc0000903就是虚拟地址的gdt基地址，但是教程中是0xc0000900，暂时没找到为什么还有3的偏移（补:原因在下面）</span></span><br><span class="line">GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000</span><br><span class="line">GDT[0x01]=Code segment, base=0x00000000, <span class="built_in">limit</span>=0xffffffff, Execute-Only, Non-Conforming, Accessed, 32-bit</span><br><span class="line">GDT[0x02]=Data segment, base=0x00000000, <span class="built_in">limit</span>=0xffffffff, Read/Write, Accessed</span><br><span class="line">GDT[0x03]=Data segment, base=0xc00b8000, <span class="built_in">limit</span>=0x00007fff, Read/Write, Accessed <span class="comment">#这里的0xc00b8000就是虚拟地址的显存段描述符的基地址</span></span><br><span class="line">You can list individual entries with <span class="string">'info gdt [NUM]'</span> or <span class="built_in">groups</span> with <span class="string">'info gdt [NUM] [NUM]'</span></span><br><span class="line"></span><br><span class="line">&lt;bochs:4&gt; info tab      <span class="comment">#查看虚拟地址的映射映射情况</span></span><br><span class="line">cr3: 0x000000100000</span><br><span class="line">0x00000000-0x000fffff -&gt; 0x000000000000-0x0000000fffff</span><br><span class="line">0xc0000000-0xc00fffff -&gt; 0x000000000000-0x0000000fffff</span><br><span class="line">0xffc00000-0xffc00fff -&gt; 0x000000101000-0x000000101fff</span><br><span class="line">0xfff00000-0xffffefff -&gt; 0x000000101000-0x0000001fffff</span><br><span class="line">0xfffff000-0xffffffff -&gt; 0x000000100000-0x000000100fff</span><br><span class="line">&lt;bochs:5&gt; </span><br></pre></td></tr></table></figure></div><p>这个0xc0000903的问题只能暂时搁置了，<strong>目前没有找到原因，如果有人知道原因可以在下方留言，非常感谢！</strong></p><ul><li>ps: 如果loader.S开头运用了jmp loader_start这一命令，gdt表的基地址就会发生偏移，可能会往后偏移，导致后面的tss添加的时候不能以0x900作为gdt基地址，我也是在这里出错的时候才想到这有问题，解决办法会在第十一章(我blog的第四节)</li></ul><hr><h4 id="G2-4-快表-TLB-简介"><a href="#G2-4-快表-TLB-简介" class="headerlink" title="G2.4 快表 TLB 简介"></a>G2.4 快表 TLB 简介</h4><p>分页机制虽然很灵活,但是要实现虚拟地址到物理地址的映射，还是很麻烦的（每一个虚拟地址到物理地址的转换都要重复以上过程）<br>所以<strong>处理器准备了一个高速缓存，可以匹配高速的处理器速率和低速的内存访问速度，它专门用来存放虚拟地址页框与物理地址页框的映射关系，这个调整缓存就是 TLB</strong>(即<br>Translation Lookaside Buffer，俗称快表)</p><blockquote><p>指令 invlpg 的操作数也是虚拟地址，其指令格式为 invlpg m。注意，<strong>其中 m 表示操作数为虚拟内存地址，并不是立即数</strong>，比如要更新虚拟地址 0x1234 对应的条目，指令为 invlpg [0x1234]，并不是 invlpg 0x1234。<strong>将来我们在内存管理系统中会涉及到 TLB 的更新操作</strong>，这一点应注意。</p></blockquote><h2 id="H-内核前的小结"><a href="#H-内核前的小结" class="headerlink" title="H 内核前的小结"></a>H 内核前的小结</h2><p>经过前面漫长的学习，尤其是对于汇编不精的我来说也是相当路漫漫，终于到了有关内核相关的内容了，所以这个放到下一篇内容中记录学习<br>当然，在这个过程中，也是让我一个非科班的人了解了更多与计算机相关的知识(因为平常可能不太喜欢看啃别的长长的书)</p><p>(ps：好想要个实习)</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li>[操作系统真象还原 (郑纲) (Z-Library)]   — 大家可以自己在网上查找相关资源</li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#x34;&#54;&#55;&#x30;&#50;&#x33;&#x36;&#x32;&#64;&#113;&#113;&#x2e;&#x63;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">因为本身自己并不是计算机相关专业的学生，所以欠缺一些专业课的学习，所以打算通过学习《操作系统真象还原》这本书来深入学习了解一下操作系统，本篇文章主要用来记录学习过程中遇到的问题，以及相关知识概念，方便后来查询......</summary>
    
    
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/categories/OS/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/OS/Linux/"/>
    
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="OS" scheme="https://blog.haozi-haozi.cn/tags/OS/"/>
    
    <category term="《真相还原》" scheme="https://blog.haozi-haozi.cn/tags/%E3%80%8A%E7%9C%9F%E7%9B%B8%E8%BF%98%E5%8E%9F%E3%80%8B/"/>
    
  </entry>
  
  <entry>
    <title>基于 i.MX6ULL-Linux的智能家居项目 &amp; Linux智能车载控制系统(Qt) -</title>
    <link href="https://blog.haozi-haozi.cn/2025/10/12/embedded_imx6ull_smart_home_qt_tcp/"/>
    <id>https://blog.haozi-haozi.cn/2025/10/12/embedded_imx6ull_smart_home_qt_tcp/</id>
    <published>2025-10-12T07:52:45.000Z</published>
    <updated>2026-03-24T08:58:02.203Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A 前情提要"></a>A 前情提要</h2><p>这里是基于我之前<strong>构建的linux系统和写的驱动为基础</strong>的，如果只是为了学习QT，大家当然也可以根据正点原子手册里的教程使用出厂系统来编写驱动，会更加简单，在最后面，结合多媒体，以及更多的驱动来实现相关拓展内容……</p><p>这里主要使用到的内容是:</p><ul><li><p><strong>嵌入式QT的编译运行环境</strong></p></li><li><p><strong>支持QT的根文件系统</strong></p></li><li><p><strong>触摸屏驱动</strong>(gt911)</p></li><li><p><strong>提供好读写接口的 LED 蜂鸣器 ADC 驱动</strong></p></li><li><p><strong>一个云服务器</strong>(主要来挂载html网页和py后端程序，如果再本地部署当然也可以)</p></li></ul><p>这里先贴一个最终效果图:</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/Embedded_1/word_00001_067.webp" width="700"><p>这里<strong>默认除了QT界面和前后端代码以外的驱动及环境已经OK了，并且有相关基础~</strong><br>完整的代码会贴在最后部分……</p><h2 id="B-QT相关"><a href="#B-QT相关" class="headerlink" title="B QT相关"></a>B QT相关</h2><h3 id="B-1-图形化配置基本控件"><a href="#B-1-图形化配置基本控件" class="headerlink" title="B.1 图形化配置基本控件"></a>B.1 图形化配置基本控件</h3><p>这里的布局相对简单，所以直接使用QT的图形化ui进行布局:<br>大致的样子如下: 有<strong>三个button</strong>，其它都是<strong>label标签</strong>，然后分别对每个标签进行命名，方便后面进行操作</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/Embedded_1/word_00001_068.webp" width="700"><h3 id="B-2-控件逻辑相关"><a href="#B-2-控件逻辑相关" class="headerlink" title="B.2 控件逻辑相关"></a>B.2 控件逻辑相关</h3><p>在这个界面，主要是要实现这三个按钮和一个更新温度的逻辑: <strong>开关LED 开关蜂鸣器 连接/断开网络 更新温度</strong> </p><p>这里先在头文件的<strong>添加4个槽函数</strong>和<strong>相关flag变量</strong>:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*部分代码*/</span></span><br><span class="line">public:</span><br><span class="line">    MainWindow(QWidget *parent = nullptr);</span><br><span class="line">    ~MainWindow();</span><br><span class="line">    <span class="type">bool</span> led1_flag;</span><br><span class="line">    <span class="type">bool</span> buzzer_flag;</span><br><span class="line">    <span class="type">bool</span> tcp_flag;</span><br><span class="line"></span><br><span class="line">private slots:</span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Set_LED1</span><span class="params">(<span class="type">bool</span> flag)</span>;           <span class="comment">//设置LED</span></span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Set_Buzzer</span><span class="params">(<span class="type">bool</span> flag)</span>;         <span class="comment">//设置蜂鸣器</span></span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Temperature_Update</span><span class="params">(<span class="type">void</span>)</span>;      <span class="comment">//更新温度</span></span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Set_Tcp_Server</span><span class="params">(<span class="type">bool</span> flag)</span>;     <span class="comment">//连接或断开服务器连接</span></span><br></pre></td></tr></table></figure></div><p>这四个函数就可以完成整体的逻辑，这里使用 <strong>Lambda 表达式</strong>来实现槽函数,下面给出一个控制LED的例子</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">MainWindow::led1_flag = <span class="literal">false</span>; <span class="comment">//默认关闭</span></span><br><span class="line"><span class="comment">//当按钮按下，执行Set_LED1() 并且切换按钮的文字，如果设置失败，提示error</span></span><br><span class="line">connect(ui-&gt;pushbutton_led1, &amp;QPushButton::clicked, this, [this]() </span><br><span class="line">{</span><br><span class="line">    <span class="type">bool</span> newState = !MainWindow::led1_flag;</span><br><span class="line">    <span class="keyword">if</span> (Set_LED1(newState)) </span><br><span class="line">    {</span><br><span class="line">        MainWindow::led1_flag = newState;</span><br><span class="line">        ui-&gt;pushbutton_led1-&gt;setText(newState ? <span class="string">"关闭LED1"</span> : <span class="string">"打开LED1"</span>);</span><br><span class="line">    } <span class="keyword">else</span> </span><br><span class="line">    {</span><br><span class="line">        QMessageBox::critical(this, QStringLiteral(<span class="string">"错误"</span>), QStringLiteral(<span class="string">"设置 LED1 失败"</span>));</span><br><span class="line">    }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">/*......*/</span></span><br></pre></td></tr></table></figure></div><p>同理蜂鸣器和TCP的连接和断开也可以这么实现</p><p>然后是 <strong>更新温度</strong> 相关，因为温度并不是按键获取，而是ADC内部获取，所以这里选择<strong>使用定时器，3s自动获取一次ADC值然后更新出来</strong>(因为这是一个模拟项目，所以我们只获取转化为电压，而不是转化为温度)</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*......*/</span></span><br><span class="line">timer = new QTimer(this);   <span class="comment">//注意在头文件定义一个 QTimer timer;</span></span><br><span class="line"><span class="comment">/*......*/</span></span><br><span class="line">timer-&gt;start(<span class="number">3000</span>);         <span class="comment">//启动定时器3000ms后触发信号</span></span><br><span class="line"></span><br><span class="line">connect(timer,&amp;QTimer::timeout,this,[=](){</span><br><span class="line">    <span class="keyword">if</span> ( !Temperature_Update() )</span><br><span class="line">    {</span><br><span class="line">        ui-&gt;label_temp-&gt;setText(<span class="string">"获取失败"</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    {</span><br><span class="line">        <span class="comment">//这是后面网络相关的，即如果tcp已经连接，就将更新的温度值发送给服务器处理</span></span><br><span class="line">        <span class="keyword">if</span>(MainWindow::tcp_flag == <span class="literal">true</span>)</span><br><span class="line">        {</span><br><span class="line">            QString tempData = ui-&gt;label_temp-&gt;text(); </span><br><span class="line">            QByteArray out = tempData.toUtf8();</span><br><span class="line">            tcpSocket-&gt;write(out);</span><br><span class="line">            tcpSocket-&gt;flush();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><p>最后将一个槽函数空实现(return true)，运行调试就可以得到一个基础的界面</p><h3 id="B-3-读写imx6u外设相关"><a href="#B-3-读写imx6u外设相关" class="headerlink" title="B.3 读写imx6u外设相关"></a>B.3 读写imx6u外设相关</h3><p>这里来实现控制LED，蜂鸣器等，<strong>Linux一切皆文件</strong>，所以控制这些外设也是<strong>通过读写文件来完成</strong>,这里不涉及LED等的驱动编写，主要介绍如何读写</p><p>首先还是以LED为例子,下面是部分LED驱动的部分代码(使用的字符设备驱动，如果是出厂系统使用的是led子系统，读写路径不同)：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*......*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//这里是驱动给外界提供的写接口，我们可以通过C库函数的write来写文件</span></span><br><span class="line"><span class="type">static</span> <span class="type">ssize_t</span>  <span class="title function_">gpioled_write</span><span class="params">(<span class="keyword">struct</span> file *filp,<span class="type">const</span> <span class="type">char</span> __user *buf,<span class="type">size_t</span> count,<span class="type">loff_t</span> *ppos)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">gpio_dev</span> *<span class="title">dev</span> =</span> (<span class="keyword">struct</span> gpio_dev *)filp-&gt;private_data;</span><br><span class="line">    <span class="type">int</span> retvalue;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> databuf[<span class="number">1</span>];</span><br><span class="line">    retvalue = copy_from_user(databuf,buf,count);</span><br><span class="line">    <span class="keyword">if</span>(retvalue &lt; <span class="number">0</span>)</span><br><span class="line">    {</span><br><span class="line">        printk(<span class="string">"kernel write failed!\r\n"</span>);</span><br><span class="line">        <span class="keyword">return</span> -EFAULT;</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">//判断开灯关灯</span></span><br><span class="line">    led_switch(databuf[<span class="number">0</span>]);</span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*......*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//自动创建设备节点，然后我们就会在linux下的 /dev/GPIOLED_NAME 的位置操作文件来控制外设 </span></span><br><span class="line">gpioled.class = class_create(THIS_MODULE,GPIOLED_NAME);</span><br><span class="line"><span class="keyword">if</span>(IS_ERR(gpioled.class))</span><br><span class="line">{</span><br><span class="line">    ret = PTR_ERR(gpioled.class);</span><br><span class="line">    <span class="keyword">goto</span> fail_class;</span><br><span class="line">}</span><br><span class="line">gpioled.device = device_create(gpioled.class,<span class="literal">NULL</span>,gpioled.devid,<span class="literal">NULL</span>,GPIOLED_NAME);</span><br><span class="line"><span class="keyword">if</span>(IS_ERR(gpioled.device))</span><br><span class="line">{</span><br><span class="line">    ret = PTR_ERR(gpioled.device);</span><br><span class="line">    <span class="keyword">goto</span> fail_device;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*......*/</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">led_switch</span><span class="params">(u8 sta)</span></span><br><span class="line">{</span><br><span class="line">    u32 val = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span>(sta == LED_ON) </span><br><span class="line">    {</span><br><span class="line">        gpio_set_value(gpioled.led_gpio,<span class="number">0</span>);   <span class="comment">//低电平点亮</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(sta == LED_OFF)</span><br><span class="line">    {</span><br><span class="line">        gpio_set_value(gpioled.led_gpio,<span class="number">1</span>);  </span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*......*/</span></span><br></pre></td></tr></table></figure></div><p>通过驱动提供的接口，我们就可以在linux下对 <code>/dev/GPIOLED_NAME</code> 文件写入一个 <code>LED_ON</code> ，那么灯就会打开，反之输入 <code>LED_OFF</code> 就会关闭</p><p>注意<strong>GPIOLED_NAME等都是宏，需要你自己在代码中定义设置</strong></p><p>所以在<strong>QT下我们使用QFile来读写文件</strong>，其实是非常简单的，举个栗子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">MainWindow::Set_LED1</span><span class="params">(<span class="type">bool</span> flag)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">bool</span> ret = <span class="literal">true</span>;</span><br><span class="line">    QFile <span class="title function_">file</span><span class="params">(<span class="string">"/dev/user_gpio_led"</span>)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以写入方式打开设备文件</span></span><br><span class="line">    <span class="keyword">if</span> (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {</span><br><span class="line">        qDebug() &lt;&lt; <span class="string">"无法打开LED设备文件"</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 根据flag值写入对应的控制信号</span></span><br><span class="line">    QTextStream <span class="title function_">out</span><span class="params">(&amp;file)</span>;</span><br><span class="line">    <span class="keyword">if</span> (flag) {</span><br><span class="line">        out &lt;&lt; <span class="string">"1"</span>;  <span class="comment">// 打开LED</span></span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        out &lt;&lt; <span class="string">"0"</span>;  <span class="comment">// 关闭LED</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    file.close();</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>同理蜂鸣器也是这样写的，就不列出来了，最后是ADC模块,首先这里使用的<strong>ADC的驱动是linux内核自带的</strong>，IIO子系统相关，我只是配置了设备树，然后加载相关驱动就可以使用</p><p>在 <code>/sys/bus/iio/devices</code> 里面会有一个设备 <code>iio:device0</code>,进入该目录,会看到很多文件，比如<code>in_voltage0_raw in_voltage1_raw in_voltage_scale</code>等，其中<strong>in_voltage1_raw 是ADC1 通道 1 原始值文件，in_voltage_scale是ADC1 比例文件(分辨率)，单位为 mV。实际电压值(mV)=in_voltage1_raw</strong>，通过用cat读取这两个文件的值，然后相乘，就可以得到ADC读取到的电压值，同理在QT里面也是如此读<br>但这里涉及到了一个小问题，就是读取该文件之后，其实当前的路径只是一个链接，如果直接使用，可能会失效，所以这里可以使用pwd来查看一下实际的路径，比如我这里的路径就是:<code>/sys/devices/platform/soc/2100000.aips-bus/2198000.adc/iio:device0</code></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">MainWindow::Temperature_Update</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">bool</span> ret = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用完整的设备路径</span></span><br><span class="line">    QString basePath = <span class="string">"/sys/devices/platform/soc/2100000.aips-bus/2198000.adc/iio:device0"</span>;</span><br><span class="line">    QFile <span class="title function_">rawFile</span><span class="params">(basePath + <span class="string">"/in_voltage1_raw"</span>)</span>;</span><br><span class="line">    QFile <span class="title function_">scaleFile</span><span class="params">(basePath + <span class="string">"/in_voltage_scale"</span>)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取原始电压值</span></span><br><span class="line">    <span class="keyword">if</span> (!rawFile.open(QIODevice::ReadOnly | QIODevice::Text)) {</span><br><span class="line">        qDebug() &lt;&lt; <span class="string">"无法打开原始电压文件:"</span> &lt;&lt; rawFile.errorString();</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    QString rawStr = QTextStream(&amp;rawFile).readLine().trimmed();</span><br><span class="line">    rawFile.close();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取电压缩放比例</span></span><br><span class="line">    <span class="keyword">if</span> (!scaleFile.open(QIODevice::ReadOnly | QIODevice::Text)) {</span><br><span class="line">        qDebug() &lt;&lt; <span class="string">"无法打开电压缩放文件:"</span> &lt;&lt; scaleFile.errorString();</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    QString scaleStr = QTextStream(&amp;scaleFile).readLine().trimmed();</span><br><span class="line">    scaleFile.close();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 转换为数值</span></span><br><span class="line">    <span class="type">bool</span> ok1, ok2;</span><br><span class="line">    <span class="type">double</span> rawValue = rawStr.toDouble(&amp;ok1);</span><br><span class="line">    <span class="type">double</span> scaleValue = scaleStr.toDouble(&amp;ok2);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!ok1 || !ok2) {</span><br><span class="line">        qDebug() &lt;&lt; <span class="string">"数据转换错误: raw="</span> &lt;&lt; rawStr &lt;&lt; <span class="string">"scale="</span> &lt;&lt; scaleStr;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算实际电压值（单位：毫伏 mV）</span></span><br><span class="line">    <span class="type">double</span> voltage_mV = rawValue * scaleValue;</span><br><span class="line">    <span class="type">double</span> voltage_V = voltage_mV / <span class="number">1000.0</span>; <span class="comment">// 转换为伏特 V</span></span><br><span class="line"></span><br><span class="line">    qDebug() &lt;&lt; <span class="string">"Debug Info - Raw:"</span> &lt;&lt; rawValue &lt;&lt; <span class="string">"Scale:"</span> &lt;&lt; scaleValue &lt;&lt; <span class="string">"Voltage:"</span> &lt;&lt; voltage_mV &lt;&lt; <span class="string">"mV"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//保留更多小数位方便观察</span></span><br><span class="line">    QString temperatureText = QString(<span class="string">" %1 V"</span>).arg(voltage_V, <span class="number">0</span>, <span class="string">'f'</span>, <span class="number">6</span>);</span><br><span class="line">    ui-&gt;label_temp-&gt;setText(temperatureText);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>读取文件后简单计算就可以获得对应的电压值了，<strong>通过以上内容，基本可以实现在触摸屏上来控制LED和蜂鸣器，以及显示温度，这里就可以进行第二步调试，如果有问题需要检查led等驱动和qt的读写文件是不是有问题</strong>，下面就是物联网现相关的代码了……</p><h3 id="B-4-物联网相关"><a href="#B-4-物联网相关" class="headerlink" title="B.4 物联网相关"></a>B.4 物联网相关</h3><p>在实现上面的基础控制之后，就可以来添加物联网功能了，首先这里假设服务器已经搭建完毕，我们只用负责去连接，这里选择TCP连接，也是<strong>使用QT的QTcpSocket等互联网相关库</strong>,注意在pro文件中添加<code>QT       += core gui network</code></p><p>这里还是主要实现几个函数: <strong>连接/断开服务器</strong>和<strong>获取本地IP</strong>，以及<strong>处理TCP接收到的指令</strong><br>首先先看一下如何<strong>获取本地IP</strong>,这个ip地址要在后面连接的时候使用</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">QString <span class="title function_">MainWindow::getLocalHostIP</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    QList&lt;QHostAddress&gt; ipAddressesList = QNetworkInterface::allAddresses();</span><br><span class="line">    QString localIP;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历所有IP地址，优先选择IPv4且非回环地址</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; ipAddressesList.size(); ++i) </span><br><span class="line">    {</span><br><span class="line">        QHostAddress ipAddr = ipAddressesList.at(i);</span><br><span class="line">        <span class="comment">// 筛选条件：IPv4协议、不是本地回环地址、不是空地址</span></span><br><span class="line">        <span class="keyword">if</span> (ipAddr.protocol() == QAbstractSocket::IPv4Protocol &amp;&amp;</span><br><span class="line">            ipAddr != QHostAddress::Null &amp;&amp;</span><br><span class="line">            ipAddr != QHostAddress::LocalHost &amp;&amp;</span><br><span class="line">            !ipAddr.toString().startsWith(<span class="string">"127.0."</span>) &amp;&amp;</span><br><span class="line">            !ipAddr.toString().startsWith(<span class="string">"169."</span>)) </span><br><span class="line">        { <span class="comment">// 排除APIPA地址</span></span><br><span class="line">            localIP = ipAddr.toString();</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">// 如果没有找到合适的IP，使用本地回环地址作为备选</span></span><br><span class="line">    <span class="keyword">if</span> (localIP.isEmpty()) </span><br><span class="line">    {</span><br><span class="line">        localIP = <span class="string">"127.0.0.1"</span>;</span><br><span class="line">        qDebug() &lt;&lt; <span class="string">"Warning: Using loopback address, no valid IPv4 address found"</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    qDebug() &lt;&lt; <span class="string">"Local IP Address:"</span> &lt;&lt; localIP;</span><br><span class="line">    <span class="keyword">return</span> localIP;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后就是<em>连接远程服务器</em>:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">MainWindow::Set_Tcp_Server</span><span class="params">(<span class="type">bool</span> flag)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">bool</span> ret = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">if</span>(flag) </span><br><span class="line">    {</span><br><span class="line">        <span class="comment">// 连接服务器操作</span></span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;state() == QAbstractSocket::ConnectedState) {</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;setText(<span class="string">"已连接"</span>);</span><br><span class="line">            qDebug() &lt;&lt; <span class="string">"TCP: Already connected"</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;state() == QAbstractSocket::ConnectingState) {</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;setText(<span class="string">"正在连接中，请稍候..."</span>);</span><br><span class="line">            qDebug() &lt;&lt; <span class="string">"TCP: Connection in progress"</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取本地IP并显示</span></span><br><span class="line">        QString localIP = getLocalHostIP();</span><br><span class="line">        qDebug() &lt;&lt; <span class="string">"Local IP:"</span> &lt;&lt; localIP;</span><br><span class="line"></span><br><span class="line">        QString serverIP = <span class="string">"xxx.xxx.xxx.xxx"</span>; <span class="comment">// 自己的服务器IP</span></span><br><span class="line">        quint16 serverPort = <span class="string">"xxx"</span>;          <span class="comment">// 自己的服务器端口</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 连接信号槽</span></span><br><span class="line">        connect(tcpSocket, &amp;QTcpSocket::connected, this, [this]() {</span><br><span class="line">            qDebug() &lt;&lt; <span class="string">"TCP: Connected to server"</span>;</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;setText(<span class="string">"已连接"</span>);</span><br><span class="line">            ui-&gt;pushbutton_tcp-&gt;setText(<span class="string">"断开远程服务器"</span>);</span><br><span class="line">        });</span><br><span class="line"></span><br><span class="line">        connect(tcpSocket, &amp;QTcpSocket::disconnected, this, [this]() {</span><br><span class="line">            qDebug() &lt;&lt; <span class="string">"TCP: Disconnected from server"</span>;</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;setText(<span class="string">"未连接"</span>);</span><br><span class="line">            ui-&gt;pushbutton_tcp-&gt;setText(<span class="string">"连接远程服务器"</span>);</span><br><span class="line">            MainWindow::tcp_flag = <span class="literal">false</span>;</span><br><span class="line">        });</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 开始连接</span></span><br><span class="line">        ui-&gt;label_tcp_state-&gt;setText(<span class="string">"正在连接..."</span>);</span><br><span class="line">        tcpSocket-&gt;connectToHost(serverIP, serverPort);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待连接完成（最多等待5秒）</span></span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;waitForConnected(<span class="number">5000</span>)) {</span><br><span class="line">            ret = <span class="literal">true</span>;</span><br><span class="line">            qDebug() &lt;&lt; <span class="string">"TCP: Connection established successfully"</span>;</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            qDebug() &lt;&lt; <span class="string">"TCP: Connection failed -"</span> &lt;&lt; tcpSocket-&gt;errorString();</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;setText(<span class="string">"连接失败: "</span> + tcpSocket-&gt;errorString());</span><br><span class="line">            ret = <span class="literal">false</span>;</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// 断开连接操作</span></span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;state() == QAbstractSocket::ConnectedState) {</span><br><span class="line">            tcpSocket-&gt;disconnectFromHost();</span><br><span class="line">            <span class="keyword">if</span>(tcpSocket-&gt;state() == QAbstractSocket::UnconnectedState ||</span><br><span class="line">               tcpSocket-&gt;waitForDisconnected(<span class="number">3000</span>)) {</span><br><span class="line">                ret = <span class="literal">true</span>;</span><br><span class="line">                ui-&gt;label_tcp_state-&gt;setText(<span class="string">"未连接"</span>);</span><br><span class="line">                qDebug() &lt;&lt; <span class="string">"TCP: Disconnected successfully"</span>;</span><br><span class="line">            }</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            <span class="comment">// 如果当前没有连接，也返回成功</span></span><br><span class="line">            ret = <span class="literal">true</span>;</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;setText(<span class="string">"未连接"</span>);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 断开所有信号连接，防止重连的时候无法接收数据</span></span><br><span class="line">        <span class="comment">//tcpSocket-&gt;disconnect();</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这里主要断开连接的时候<strong>不要使用tcpSocket-&gt;disconnect()来断开所有连接</strong>，这样会导致接收TCP数据的槽函数被断开，如果imx6u断开重连后就无法接收数据,这里的接收函数如下，数据包是自己定义的</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 连接readyRead信号来处理接收到的数据</span></span><br><span class="line">    connect(tcpSocket, &amp;QTcpSocket::readyRead, this, [this]() {</span><br><span class="line">        <span class="comment">// 读取所有可用数据并追加到缓冲区</span></span><br><span class="line">        QByteArray newData = tcpSocket-&gt;readAll();</span><br><span class="line">        <span class="keyword">if</span> (!newData.isEmpty()) {</span><br><span class="line">            tcpBuf.append(newData);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 每个完整包为 3 字节： [cmd_group, cmd_id, 0xFF]</span></span><br><span class="line">        <span class="comment">// LED 开/关: [0x01, 0x01/0x02, 0xFF]</span></span><br><span class="line">        <span class="comment">// BEEP 开/关: [0x02, 0x01/0x02, 0xFF]</span></span><br><span class="line">        <span class="comment">// GET_TEMP: [0x03, 0x01, 0xFF]</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (tcpBuf.size() &gt;= <span class="number">3</span>) {</span><br><span class="line">            <span class="comment">// 检查第三字节是否为 0xFF（包尾标志）</span></span><br><span class="line">            <span class="keyword">if</span> (static_cast&lt;<span class="type">unsigned</span> <span class="type">char</span>&gt;(tcpBuf[<span class="number">2</span>]) != <span class="number">0xFF</span>) {</span><br><span class="line">                <span class="comment">// 如果第三字节不是 0xFF，说明可能出现错位或噪声</span></span><br><span class="line">                <span class="comment">// 丢弃第一个字节，继续尝试重同步</span></span><br><span class="line">                tcpBuf.remove(<span class="number">0</span>, <span class="number">1</span>);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 现在前 3 字节看起来像一包，取出进行处理</span></span><br><span class="line">            <span class="type">unsigned</span> <span class="type">char</span> b0 = static_cast&lt;<span class="type">unsigned</span> <span class="type">char</span>&gt;(tcpBuf[<span class="number">0</span>]);</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">char</span> b1 = static_cast&lt;<span class="type">unsigned</span> <span class="type">char</span>&gt;(tcpBuf[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 移除已处理的三个字节</span></span><br><span class="line">            tcpBuf.remove(<span class="number">0</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 根据命令处理</span></span><br><span class="line">            <span class="keyword">if</span> (b0 == <span class="number">0x01</span>) {</span><br><span class="line">                <span class="comment">// LED 控制</span></span><br><span class="line">                <span class="keyword">if</span> (b1 == <span class="number">0x01</span>) {</span><br><span class="line">                    <span class="comment">// 打开 LED1</span></span><br><span class="line">                    <span class="keyword">if</span> (Set_LED1(<span class="literal">true</span>)) {</span><br><span class="line">                        MainWindow::led1_flag = <span class="literal">true</span>;</span><br><span class="line">                        ui-&gt;pushbutton_led1-&gt;setText(QStringLiteral(<span class="string">"关闭LED1"</span>));</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"LED1 turned on via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"Failed to turn on LED1 via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> <span class="keyword">if</span> (b1 == <span class="number">0x02</span>) {</span><br><span class="line">                    <span class="comment">// 关闭 LED1</span></span><br><span class="line">                    <span class="keyword">if</span> (Set_LED1(<span class="literal">false</span>)) {</span><br><span class="line">                        MainWindow::led1_flag = <span class="literal">false</span>;</span><br><span class="line">                        ui-&gt;pushbutton_led1-&gt;setText(QStringLiteral(<span class="string">"打开LED1"</span>));</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"LED1 turned off via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"Failed to turn off LED1 via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> {</span><br><span class="line">                    qDebug() &lt;&lt; <span class="string">"Unknown LED command b1="</span> &lt;&lt; b1;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (b0 == <span class="number">0x02</span>) {</span><br><span class="line">                <span class="comment">// 蜂鸣器控制</span></span><br><span class="line">                <span class="keyword">if</span> (b1 == <span class="number">0x01</span>) {</span><br><span class="line">                    <span class="keyword">if</span> (Set_Buzzer(<span class="literal">true</span>)) {</span><br><span class="line">                        MainWindow::buzzer_flag = <span class="literal">true</span>;</span><br><span class="line">                        ui-&gt;pushbutton_buzzer-&gt;setText(QStringLiteral(<span class="string">"关闭蜂鸣器"</span>));</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"Buzzer turned on via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"Failed to turn on buzzer via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> <span class="keyword">if</span> (b1 == <span class="number">0x02</span>) {</span><br><span class="line">                    <span class="keyword">if</span> (Set_Buzzer(<span class="literal">false</span>)) {</span><br><span class="line">                        MainWindow::buzzer_flag = <span class="literal">false</span>;</span><br><span class="line">                        ui-&gt;pushbutton_buzzer-&gt;setText(QStringLiteral(<span class="string">"打开蜂鸣器"</span>));</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"Buzzer turned off via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        qDebug() &lt;&lt; <span class="string">"Failed to turn off buzzer via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> {</span><br><span class="line">                    qDebug() &lt;&lt; <span class="string">"Unknown BEEP command b1="</span> &lt;&lt; b1;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (b0 == <span class="number">0x03</span> &amp;&amp; b1 == <span class="number">0x01</span>) {</span><br><span class="line">                <span class="comment">// GET_TEMP 请求：刷新温度并把数据发送回服务器</span></span><br><span class="line">                <span class="keyword">if</span> (Temperature_Update()) {</span><br><span class="line">                    qDebug() &lt;&lt; <span class="string">"Temperature updated via TCP request"</span>;</span><br><span class="line">                    QString tempData = ui-&gt;label_temp-&gt;text(); <span class="comment">// 例如 "25.3"</span></span><br><span class="line">                    QByteArray out = tempData.toUtf8();</span><br><span class="line">                    tcpSocket-&gt;write(out);</span><br><span class="line">                    tcpSocket-&gt;flush();</span><br><span class="line">                } <span class="keyword">else</span> {</span><br><span class="line">                    ui-&gt;label_temp-&gt;setText(QStringLiteral(<span class="string">"获取失败"</span>));</span><br><span class="line">                    QByteArray out = QStringLiteral(<span class="string">"获取失败"</span>).toUtf8();</span><br><span class="line">                    tcpSocket-&gt;write(out);</span><br><span class="line">                    tcpSocket-&gt;flush();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> {</span><br><span class="line">                qDebug() &lt;&lt; <span class="string">"Unknown 3-byte packet: b0="</span> &lt;&lt; b0 &lt;&lt; <span class="string">" b1="</span> &lt;&lt; b1;</span><br><span class="line">                <span class="comment">// 可选择应答错误或直接忽略</span></span><br><span class="line">            }</span><br><span class="line">        } </span><br><span class="line">    });</span><br></pre></td></tr></table></figure></div><p><strong>通过解析得到的数据包然后调用相关函数就可以实现接收服务器数据，并控制相关外设或者发送数据给服务器</strong>，这样就实现了一个简易的物联网通信，当然实际项目中，大家可以使用MQTT物联网会更加方便安全</p><p>通过上面这些函数，QT的逻辑方面基本都结束了，下面的B5 B6主要是一些额外添加自选的部分</p><h3 id="B-5-添加显示时间"><a href="#B-5-添加显示时间" class="headerlink" title="B.5 添加显示时间"></a>B.5 添加显示时间</h3><p>添加显示时间很简单，只用调用<strong>QT的QDateTime库</strong>即可，举个例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">connect(timer,&amp;QTimer::timeout,this,[=](){</span><br><span class="line">    time_cnt++;</span><br><span class="line">    <span class="keyword">if</span>(time_cnt&gt;=<span class="number">3</span>)</span><br><span class="line">    {</span><br><span class="line">        time_cnt = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> ( !Temperature_Update() )</span><br><span class="line">        {</span><br><span class="line">            ui-&gt;label_temp-&gt;setText(<span class="string">"获取失败"</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        {</span><br><span class="line">            <span class="keyword">if</span>(MainWindow::tcp_flag == <span class="literal">true</span>)</span><br><span class="line">            {</span><br><span class="line">                QString tempData = ui-&gt;label_temp-&gt;text(); </span><br><span class="line">                QByteArray out = tempData.toUtf8();</span><br><span class="line">                tcpSocket-&gt;write(out);</span><br><span class="line">                tcpSocket-&gt;flush();</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">//update timer</span></span><br><span class="line">    QDateTime currentDateTime = QDateTime::currentDateTime();</span><br><span class="line">    QString timeString = currentDateTime.toString(<span class="string">"yyyy-MM-dd HH:mm:ss"</span>);</span><br><span class="line">    ui-&gt;label_time-&gt;setText(timeString);</span><br><span class="line"></span><br><span class="line">});</span><br><span class="line">timer-&gt;start(<span class="number">1000</span>);</span><br></pre></td></tr></table></figure></div><p>通过更改定时器为1s，每过1s通过 <code>QDateTime::currentDateTime()</code>获取当前时间，并转化为想要的格式显示到屏幕上即可</p><h3 id="B-6-界面简单美化"><a href="#B-6-界面简单美化" class="headerlink" title="B.6 界面简单美化"></a>B.6 界面简单美化</h3><p>这里主要使用样式表设置了一下按钮背景，字体大小，还有整体背景颜色，大家可以根据自己的喜欢来设置<br>举个例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">this-&gt;setStyleSheet(</span><br><span class="line">    <span class="string">"QMainWindow {"</span></span><br><span class="line">    <span class="string">"   background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2c3e50, stop:1 #3498db);"</span></span><br><span class="line">    <span class="string">"}"</span></span><br><span class="line">    <span class="string">"QPushButton {"</span></span><br><span class="line">    <span class="string">"   background-color: #3498db;"</span></span><br><span class="line">    <span class="string">"   border: none;"</span></span><br><span class="line">    <span class="string">"   border-radius: 5px;"</span></span><br><span class="line">    <span class="string">"   color: white;"</span></span><br><span class="line">    <span class="string">"   padding: 8px 15px;"</span></span><br><span class="line">    <span class="string">"}"</span></span><br><span class="line">    <span class="string">"QPushButton:hover {"</span></span><br><span class="line">    <span class="string">"   background-color: #2980b9;"</span></span><br><span class="line">    <span class="string">"}"</span></span><br><span class="line">    <span class="string">"QPushButton:pressed {"</span></span><br><span class="line">    <span class="string">"   background-color: #21618c;"</span></span><br><span class="line">    <span class="string">"}"</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">ui-&gt;label_title-&gt;setStyleSheet(</span><br><span class="line">    <span class="string">"QLabel {"</span></span><br><span class="line">    <span class="string">"   background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #e74c3c, stop:1 #c0392b);"</span></span><br><span class="line">    <span class="string">"   color: white;"</span></span><br><span class="line">    <span class="string">"   border-radius: 10px;"</span></span><br><span class="line">    <span class="string">"   padding: 10px;"</span></span><br><span class="line">    <span class="string">"   font-weight: bold;"</span></span><br><span class="line">    <span class="string">"}"</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure></div><h2 id="C-网页相关"><a href="#C-网页相关" class="headerlink" title="C 网页相关"></a>C 网页相关</h2><p>因为这里主要是嵌入式相关，前后端涉及的不多，所以这里使用的都是非常简单的代码,如果有前后端基础的可以自己编写更好用且更美观的界面~</p><h3 id="C-1-前端"><a href="#C-1-前端" class="headerlink" title="C.1 前端"></a>C.1 前端</h3><p>前端使用静态页面（HTML/CSS/原始javaScript）,通过浏览器的<strong>WebSocket与后端通信</strong>，通过sessionStorage做一个示例登录(不安全)</p><ul><li><strong>WebSocket 是全双工、事件驱动的协议；浏览器与后端可以任意一端主动推送数据</strong></li></ul><h3 id="C-2-后端"><a href="#C-2-后端" class="headerlink" title="C.2 后端"></a>C.2 后端</h3><p>后端使用 <strong>Python 3 + asyncio</strong>，使用<strong>第三方库 websockets 提供 WebSocket 服务</strong>，同时<strong>用 asyncio(异步) 的 TCP server 接收 imx6ull的数据</strong>并转发给网页客户端，或网页转发给imx6ull命令</p><p>这两个部分只是物联网示例，代码逻辑都非常简单，大家也可以直接用AI跑出来，具体代码贴也是贴在最后面，下面是前端的页面:</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/Embedded_1/word_00001_069.webp" width="700"><h2 id="D-imx6u驱动相关"><a href="#D-imx6u驱动相关" class="headerlink" title="D imx6u驱动相关"></a>D imx6u驱动相关</h2><p>这里主要给出链接，大家可以自己查看或者查找，有问题可以留言</p><ul><li><p>触摸屏可以参考<a href="https://blog.haozi-haozi.cn/2025/09/26/embedded_imx6ull_gt911/">基于imx6ull的gt911移植</a></p></li><li><p>LED和蜂鸣器等可以参考<a class="link" href="http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html">【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p></li><li><p><a href="https://blog.haozi-haozi.cn/2025/08/20/embedded_imx6ull/">QT移植以及其它imx6ull概念</a></p></li></ul><h2 id="E-相关问题"><a href="#E-相关问题" class="headerlink" title="E 相关问题"></a>E 相关问题</h2><ul><li><strong>在imx6ull连接再断开，再连接之后，无法响应服务器发送的消息</strong></li></ul><p>检查Set_Tcp_Server()函数中，不要调用tcpSocket-&gt;disconnect()，这样会导致槽函数也关闭</p><ul><li><strong>QT无法正确控制 led或者蜂鸣器等</strong></li></ul><p>检查Set_LED1()等函数，检查写文件写入的字符是什么类型，需要与驱动里面的类型对上，比如可以同时规定成字符’1’</p><ul><li><strong>QT无法正确读取ADC的值</strong></li></ul><p>检查QT读取ADC的打开文件函数的路径，路径尽量写成绝对地址，不要使用链接，否则可能会有问题</p><ul><li><strong>在开发板上启动的时间不对，但是确实是有网络的</strong></li></ul><p>检查rtc的时间，因为没有写网络更新函数，所以时间需要更新一次<br><code>hwclock -r</code> 查看当前时间，如果不对可以使用<code>date -s "2025-10-14 15:30:00"</code>写入当前时间，然后<code>hwclock -w</code>进行保存</p><h2 id="F-完整代码"><a href="#F-完整代码" class="headerlink" title="F 完整代码"></a>F 完整代码</h2><p>如果有整体文件需求，可以留言或者发邮箱给我</p><p>下面是具体代码:</p><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>mainwindow.h： 点击查看更多 </summary>              <div class="content">              <div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> MAINWINDOW_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAINWINDOW_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QMainWindow&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QPushButton&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QTimerEvent&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QFile&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QFont&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QTimer&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QNetworkInterface&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QDebug&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QTcpServer&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QTcpSocket&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QString&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QAbstractAnimation&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QErrorMessage&gt;</span></span></span><br><span class="line"></span><br><span class="line">QT_BEGIN_NAMESPACE</span><br><span class="line">namespace Ui { <span class="class"><span class="keyword">class</span> <span class="title">MainWindow</span>;</span> }</span><br><span class="line">QT_END_NAMESPACE</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MainWindow</span> :</span> public QMainWindow</span><br><span class="line">{</span><br><span class="line">    Q_OBJECT</span><br><span class="line"></span><br><span class="line">public:</span><br><span class="line">    MainWindow(QWidget *parent = nullptr);</span><br><span class="line">    ~MainWindow();</span><br><span class="line"></span><br><span class="line">    QTimer *timer;</span><br><span class="line">    <span class="type">bool</span> led1_flag;</span><br><span class="line">    <span class="type">bool</span> buzzer_flag;</span><br><span class="line">    <span class="type">bool</span> tcp_flag;</span><br><span class="line">    QTcpSocket *tcpSocket;</span><br><span class="line">    QByteArray tcpBuf;</span><br><span class="line"></span><br><span class="line">    QString <span class="title function_">getLocalHostIP</span><span class="params">()</span>;</span><br><span class="line">    <span class="type">int</span> time_cnt;</span><br><span class="line"></span><br><span class="line">private:</span><br><span class="line">    Ui::MainWindow *ui;</span><br><span class="line"></span><br><span class="line">private slots:</span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Set_LED1</span><span class="params">(<span class="type">bool</span> flag)</span>;</span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Set_Buzzer</span><span class="params">(<span class="type">bool</span> flag)</span>;</span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Temperature_Update</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line">    <span class="type">bool</span> <span class="title function_">Set_Tcp_Server</span><span class="params">(<span class="type">bool</span> flag)</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">};</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// MAINWINDOW_H</span></span></span><br></pre></td></tr></table></figure></div>              </div>            </details><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>mainwindow.c： 点击查看更多 </summary>              <div class="content">              <p>注意修改成自己的tcp和websocket端口号，以及修改服务器地址</p><div class="code-container" data-rel="Cpp"><figure class="iseeu highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"mainwindow.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ui_mainwindow.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QMessageBox&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QDateTime&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QTimer&gt;</span></span></span><br><span class="line"></span><br><span class="line">MainWindow::<span class="built_in">MainWindow</span>(QWidget *parent)</span><br><span class="line">    : <span class="built_in">QMainWindow</span>(parent)</span><br><span class="line">    , <span class="built_in">ui</span>(<span class="keyword">new</span> Ui::MainWindow)</span><br><span class="line">{</span><br><span class="line">    ui-&gt;<span class="built_in">setupUi</span>(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">    QFont qfont;</span><br><span class="line">    qfont.<span class="built_in">setPointSize</span>(<span class="number">40</span>);</span><br><span class="line">    ui-&gt;label_title-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line"></span><br><span class="line">    qfont.<span class="built_in">setPointSize</span>(<span class="number">16</span>);</span><br><span class="line">    ui-&gt;label_temp-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line">    ui-&gt;label_tcp_state-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line">    ui-&gt;label_temp_unuser-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line">    ui-&gt;pushbutton_tcp-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line">    ui-&gt;pushbutton_led1-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line">    ui-&gt;pushbutton_buzzer-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line"></span><br><span class="line">    ui-&gt;label_time-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line">    ui-&gt;label_time_unuser-&gt;<span class="built_in">setFont</span>(qfont);</span><br><span class="line"></span><br><span class="line">    time_cnt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    timer = <span class="keyword">new</span> <span class="built_in">QTimer</span>(<span class="keyword">this</span>);</span><br><span class="line">    tcpSocket = <span class="keyword">new</span> <span class="built_in">QTcpSocket</span>();</span><br><span class="line"></span><br><span class="line">    MainWindow::tcp_flag = <span class="literal">false</span>;</span><br><span class="line">    MainWindow::led1_flag = <span class="literal">false</span>;</span><br><span class="line">    MainWindow::buzzer_flag = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">this</span>-&gt;<span class="built_in">setStyleSheet</span>(</span><br><span class="line">        <span class="string">"QMainWindow {"</span></span><br><span class="line">        <span class="string">"   background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2c3e50, stop:1 #3498db);"</span></span><br><span class="line">        <span class="string">"}"</span></span><br><span class="line"><span class="comment">//        "QLabel {"</span></span><br><span class="line"><span class="comment">//        "   background-color: rgba(255, 255, 255, 150);"  // 半透明白色背景</span></span><br><span class="line"><span class="comment">//        "   border-radius: 8px;"</span></span><br><span class="line"><span class="comment">//        "   padding: 5px;"</span></span><br><span class="line"><span class="comment">//        "   color: #2c3e50;"</span></span><br><span class="line"><span class="comment">//        "}"</span></span><br><span class="line">        <span class="string">"QPushButton {"</span></span><br><span class="line">        <span class="string">"   background-color: #3498db;"</span></span><br><span class="line">        <span class="string">"   border: none;"</span></span><br><span class="line">        <span class="string">"   border-radius: 5px;"</span></span><br><span class="line">        <span class="string">"   color: white;"</span></span><br><span class="line">        <span class="string">"   padding: 8px 15px;"</span></span><br><span class="line">        <span class="string">"}"</span></span><br><span class="line">        <span class="string">"QPushButton:hover {"</span></span><br><span class="line">        <span class="string">"   background-color: #2980b9;"</span></span><br><span class="line">        <span class="string">"}"</span></span><br><span class="line">        <span class="string">"QPushButton:pressed {"</span></span><br><span class="line">        <span class="string">"   background-color: #21618c;"</span></span><br><span class="line">        <span class="string">"}"</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    ui-&gt;label_title-&gt;<span class="built_in">setStyleSheet</span>(</span><br><span class="line">        <span class="string">"QLabel {"</span></span><br><span class="line">        <span class="string">"   background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #e74c3c, stop:1 #c0392b);"</span></span><br><span class="line">        <span class="string">"   color: white;"</span></span><br><span class="line">        <span class="string">"   border-radius: 10px;"</span></span><br><span class="line">        <span class="string">"   padding: 10px;"</span></span><br><span class="line">        <span class="string">"   font-weight: bold;"</span></span><br><span class="line">        <span class="string">"}"</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="built_in">connect</span>(ui-&gt;pushbutton_led1, &amp;QPushButton::clicked, <span class="keyword">this</span>, [<span class="keyword">this</span>]() {</span><br><span class="line">        <span class="type">bool</span> newState = !MainWindow::led1_flag;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">Set_LED1</span>(newState)) {</span><br><span class="line">            MainWindow::led1_flag = newState;</span><br><span class="line">            ui-&gt;pushbutton_led1-&gt;<span class="built_in">setText</span>(newState ? <span class="string">"关闭LED1"</span> : <span class="string">"打开LED1"</span>);</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            QMessageBox::<span class="built_in">critical</span>(<span class="keyword">this</span>, <span class="built_in">QStringLiteral</span>(<span class="string">"错误"</span>), <span class="built_in">QStringLiteral</span>(<span class="string">"设置 LED1 失败"</span>));</span><br><span class="line">        }</span><br><span class="line">    });</span><br><span class="line"></span><br><span class="line">    <span class="built_in">connect</span>(ui-&gt;pushbutton_buzzer, &amp;QPushButton::clicked, <span class="keyword">this</span>, [<span class="keyword">this</span>]() {</span><br><span class="line">        <span class="type">bool</span> newState = !MainWindow::buzzer_flag;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">Set_Buzzer</span>(newState)) {</span><br><span class="line">            MainWindow::buzzer_flag = newState;</span><br><span class="line">            ui-&gt;pushbutton_buzzer-&gt;<span class="built_in">setText</span>(newState ? <span class="string">"关闭蜂鸣器"</span> : <span class="string">"打开蜂鸣器"</span>);</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            QMessageBox::<span class="built_in">critical</span>(<span class="keyword">this</span>, <span class="built_in">QStringLiteral</span>(<span class="string">"错误"</span>), <span class="built_in">QStringLiteral</span>(<span class="string">"设置蜂鸣器失败"</span>));</span><br><span class="line">        }</span><br><span class="line">    });</span><br><span class="line"></span><br><span class="line">    <span class="built_in">connect</span>(ui-&gt;pushbutton_tcp, &amp;QPushButton::clicked, <span class="keyword">this</span>, [<span class="keyword">this</span>]() {</span><br><span class="line">        <span class="type">bool</span> newState = !MainWindow::tcp_flag;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">Set_Tcp_Server</span>(newState)) {</span><br><span class="line">            MainWindow::tcp_flag = newState;</span><br><span class="line">            ui-&gt;pushbutton_tcp-&gt;<span class="built_in">setText</span>(newState ? <span class="string">"断开远程服务器"</span> : <span class="string">"连接远程服务器"</span>);</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            QMessageBox::<span class="built_in">critical</span>(<span class="keyword">this</span>, <span class="built_in">QStringLiteral</span>(<span class="string">"错误"</span>), <span class="built_in">QStringLiteral</span>(<span class="string">"连接/断开远程服务器失败"</span>));</span><br><span class="line">            <span class="comment">// 更新 UI 状态以反映实际连接状态</span></span><br><span class="line">            ui-&gt;pushbutton_tcp-&gt;<span class="built_in">setText</span>(MainWindow::tcp_flag ? <span class="string">"断开远程服务器"</span> : <span class="string">"连接远程服务器"</span>);</span><br><span class="line">        }</span><br><span class="line">    });</span><br><span class="line"></span><br><span class="line">    <span class="built_in">connect</span>(timer,&amp;QTimer::timeout,<span class="keyword">this</span>,[=](){</span><br><span class="line">        time_cnt++;</span><br><span class="line">        <span class="keyword">if</span>(time_cnt&gt;=<span class="number">3</span>)</span><br><span class="line">        {</span><br><span class="line">            time_cnt = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">if</span> ( !<span class="built_in">Temperature_Update</span>() )</span><br><span class="line">            {</span><br><span class="line">                ui-&gt;label_temp-&gt;<span class="built_in">setText</span>(<span class="string">"获取失败"</span>);</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            {</span><br><span class="line">                <span class="keyword">if</span>(MainWindow::tcp_flag == <span class="literal">true</span>)</span><br><span class="line">                {</span><br><span class="line">                    QString tempData = ui-&gt;label_temp-&gt;<span class="built_in">text</span>(); <span class="comment">// 例如 "25.3"</span></span><br><span class="line">                    QByteArray out = tempData.<span class="built_in">toUtf8</span>();</span><br><span class="line">                    tcpSocket-&gt;<span class="built_in">write</span>(out);</span><br><span class="line">                    tcpSocket-&gt;<span class="built_in">flush</span>();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">//update timer</span></span><br><span class="line">        QDateTime currentDateTime = QDateTime::<span class="built_in">currentDateTime</span>();</span><br><span class="line">        QString timeString = currentDateTime.<span class="built_in">toString</span>(<span class="string">"yyyy-MM-dd HH:mm:ss"</span>);</span><br><span class="line">        ui-&gt;label_time-&gt;<span class="built_in">setText</span>(timeString);</span><br><span class="line"></span><br><span class="line">    });</span><br><span class="line">    timer-&gt;<span class="built_in">start</span>(<span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 连接readyRead信号来处理接收到的数据</span></span><br><span class="line">    <span class="built_in">connect</span>(tcpSocket, &amp;QTcpSocket::readyRead, <span class="keyword">this</span>, [<span class="keyword">this</span>]() {</span><br><span class="line">        <span class="comment">// 读取所有可用数据并追加到缓冲区</span></span><br><span class="line">        QByteArray newData = tcpSocket-&gt;<span class="built_in">readAll</span>();</span><br><span class="line">        <span class="keyword">if</span> (!newData.<span class="built_in">isEmpty</span>()) {</span><br><span class="line">            tcpBuf.<span class="built_in">append</span>(newData);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 每个完整包为 3 字节： [cmd_group, cmd_id, 0xFF]</span></span><br><span class="line">        <span class="comment">// LED 开/关: [0x01, 0x01/0x02, 0xFF]</span></span><br><span class="line">        <span class="comment">// BEEP 开/关: [0x02, 0x01/0x02, 0xFF]</span></span><br><span class="line">        <span class="comment">// GET_TEMP: [0x03, 0x01, 0xFF]</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (tcpBuf.<span class="built_in">size</span>() &gt;= <span class="number">3</span>) {</span><br><span class="line">            <span class="comment">// 检查第三字节是否为 0xFF（包尾标志）</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">static_cast</span>&lt;<span class="type">unsigned</span> <span class="type">char</span>&gt;(tcpBuf[<span class="number">2</span>]) != <span class="number">0xFF</span>) {</span><br><span class="line">                <span class="comment">// 如果第三字节不是 0xFF，说明可能出现错位或噪声</span></span><br><span class="line">                <span class="comment">// 丢弃第一个字节，继续尝试重同步</span></span><br><span class="line">                tcpBuf.<span class="built_in">remove</span>(<span class="number">0</span>, <span class="number">1</span>);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 现在前 3 字节看起来像一包，取出进行处理</span></span><br><span class="line">            <span class="type">unsigned</span> <span class="type">char</span> b0 = <span class="built_in">static_cast</span>&lt;<span class="type">unsigned</span> <span class="type">char</span>&gt;(tcpBuf[<span class="number">0</span>]);</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">char</span> b1 = <span class="built_in">static_cast</span>&lt;<span class="type">unsigned</span> <span class="type">char</span>&gt;(tcpBuf[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 移除已处理的三个字节</span></span><br><span class="line">            tcpBuf.<span class="built_in">remove</span>(<span class="number">0</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 根据命令处理</span></span><br><span class="line">            <span class="keyword">if</span> (b0 == <span class="number">0x01</span>) {</span><br><span class="line">                <span class="comment">// LED 控制</span></span><br><span class="line">                <span class="keyword">if</span> (b1 == <span class="number">0x01</span>) {</span><br><span class="line">                    <span class="comment">// 打开 LED1</span></span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">Set_LED1</span>(<span class="literal">true</span>)) {</span><br><span class="line">                        MainWindow::led1_flag = <span class="literal">true</span>;</span><br><span class="line">                        ui-&gt;pushbutton_led1-&gt;<span class="built_in">setText</span>(<span class="built_in">QStringLiteral</span>(<span class="string">"关闭LED1"</span>));</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"LED1 turned on via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Failed to turn on LED1 via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> <span class="keyword">if</span> (b1 == <span class="number">0x02</span>) {</span><br><span class="line">                    <span class="comment">// 关闭 LED1</span></span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">Set_LED1</span>(<span class="literal">false</span>)) {</span><br><span class="line">                        MainWindow::led1_flag = <span class="literal">false</span>;</span><br><span class="line">                        ui-&gt;pushbutton_led1-&gt;<span class="built_in">setText</span>(<span class="built_in">QStringLiteral</span>(<span class="string">"打开LED1"</span>));</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"LED1 turned off via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Failed to turn off LED1 via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> {</span><br><span class="line">                    <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Unknown LED command b1="</span> &lt;&lt; b1;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (b0 == <span class="number">0x02</span>) {</span><br><span class="line">                <span class="comment">// 蜂鸣器控制</span></span><br><span class="line">                <span class="keyword">if</span> (b1 == <span class="number">0x01</span>) {</span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">Set_Buzzer</span>(<span class="literal">true</span>)) {</span><br><span class="line">                        MainWindow::buzzer_flag = <span class="literal">true</span>;</span><br><span class="line">                        ui-&gt;pushbutton_buzzer-&gt;<span class="built_in">setText</span>(<span class="built_in">QStringLiteral</span>(<span class="string">"关闭蜂鸣器"</span>));</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Buzzer turned on via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Failed to turn on buzzer via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> <span class="keyword">if</span> (b1 == <span class="number">0x02</span>) {</span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">Set_Buzzer</span>(<span class="literal">false</span>)) {</span><br><span class="line">                        MainWindow::buzzer_flag = <span class="literal">false</span>;</span><br><span class="line">                        ui-&gt;pushbutton_buzzer-&gt;<span class="built_in">setText</span>(<span class="built_in">QStringLiteral</span>(<span class="string">"打开蜂鸣器"</span>));</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Buzzer turned off via 3-byte packet"</span>;</span><br><span class="line">                    } <span class="keyword">else</span> {</span><br><span class="line">                        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Failed to turn off buzzer via TCP packet"</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> {</span><br><span class="line">                    <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Unknown BEEP command b1="</span> &lt;&lt; b1;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (b0 == <span class="number">0x03</span> &amp;&amp; b1 == <span class="number">0x01</span>) {</span><br><span class="line">                <span class="comment">// GET_TEMP 请求：刷新温度并把数据发送回服务器（保持你原来的行为）</span></span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">Temperature_Update</span>()) {</span><br><span class="line">                    <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Temperature updated via TCP request"</span>;</span><br><span class="line">                    QString tempData = ui-&gt;label_temp-&gt;<span class="built_in">text</span>(); <span class="comment">// 例如 "25.3"</span></span><br><span class="line">                    QByteArray out = tempData.<span class="built_in">toUtf8</span>();</span><br><span class="line">                    tcpSocket-&gt;<span class="built_in">write</span>(out);</span><br><span class="line">                    tcpSocket-&gt;<span class="built_in">flush</span>();</span><br><span class="line">                } <span class="keyword">else</span> {</span><br><span class="line">                    ui-&gt;label_temp-&gt;<span class="built_in">setText</span>(<span class="built_in">QStringLiteral</span>(<span class="string">"获取失败"</span>));</span><br><span class="line">                    QByteArray out = <span class="built_in">QStringLiteral</span>(<span class="string">"获取失败"</span>).<span class="built_in">toUtf8</span>();</span><br><span class="line">                    tcpSocket-&gt;<span class="built_in">write</span>(out);</span><br><span class="line">                    tcpSocket-&gt;<span class="built_in">flush</span>();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> {</span><br><span class="line">                <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Unknown 3-byte packet: b0="</span> &lt;&lt; b0 &lt;&lt; <span class="string">" b1="</span> &lt;&lt; b1;</span><br><span class="line">                <span class="comment">// 可选择应答错误或直接忽略</span></span><br><span class="line">            }</span><br><span class="line">        } <span class="comment">// end while</span></span><br><span class="line">    });</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">MainWindow::~<span class="built_in">MainWindow</span>()</span><br><span class="line">{</span><br><span class="line">    <span class="keyword">delete</span> ui;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">MainWindow::Set_LED1</span><span class="params">(<span class="type">bool</span> flag)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">    <span class="type">bool</span> ret = <span class="literal">true</span>;</span><br><span class="line">    <span class="function">QFile <span class="title">file</span><span class="params">(<span class="string">"/dev/user_gpio_led"</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以写入方式打开设备文件</span></span><br><span class="line">    <span class="keyword">if</span> (!file.<span class="built_in">open</span>(QIODevice::WriteOnly | QIODevice::Text)) {</span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"无法打开LED设备文件"</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 根据flag值写入对应的控制信号</span></span><br><span class="line">    <span class="function">QTextStream <span class="title">out</span><span class="params">(&amp;file)</span></span>;</span><br><span class="line">    <span class="keyword">if</span> (flag) {</span><br><span class="line">        out &lt;&lt; <span class="string">"1"</span>;  <span class="comment">// 打开LED</span></span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        out &lt;&lt; <span class="string">"0"</span>;  <span class="comment">// 关闭LED</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    file.<span class="built_in">close</span>();</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">MainWindow::Set_Buzzer</span><span class="params">(<span class="type">bool</span> flag)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">    <span class="type">bool</span> ret = <span class="literal">true</span>;</span><br><span class="line">    <span class="function">QFile <span class="title">file</span><span class="params">(<span class="string">"/dev/user_gpio_buzzer"</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以写入方式打开设备文件</span></span><br><span class="line">    <span class="keyword">if</span> (!file.<span class="built_in">open</span>(QIODevice::WriteOnly | QIODevice::Text)) {</span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"无法打开蜂鸣器设备文件:"</span> &lt;&lt; file.<span class="built_in">errorString</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 根据flag值写入对应的控制信号</span></span><br><span class="line">    <span class="function">QTextStream <span class="title">out</span><span class="params">(&amp;file)</span></span>;</span><br><span class="line">    <span class="keyword">if</span> (flag) {</span><br><span class="line">        out &lt;&lt; <span class="string">"1"</span>;  <span class="comment">// 打开蜂鸣器</span></span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"蜂鸣器已打开"</span>;</span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        out &lt;&lt; <span class="string">"0"</span>;  <span class="comment">// 关闭蜂鸣器</span></span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"蜂鸣器已关闭"</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    file.<span class="built_in">close</span>();</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">MainWindow::Set_Tcp_Server</span><span class="params">(<span class="type">bool</span> flag)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">    <span class="type">bool</span> ret = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(flag) {</span><br><span class="line">        <span class="comment">// 连接服务器操作</span></span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;<span class="built_in">state</span>() == QAbstractSocket::ConnectedState) {</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"已连接"</span>);</span><br><span class="line">            <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"TCP: Already connected"</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;<span class="built_in">state</span>() == QAbstractSocket::ConnectingState) {</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"正在连接中，请稍候..."</span>);</span><br><span class="line">            <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"TCP: Connection in progress"</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取本地IP并显示</span></span><br><span class="line">        QString localIP = <span class="built_in">getLocalHostIP</span>();</span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Local IP:"</span> &lt;&lt; localIP;</span><br><span class="line"></span><br><span class="line">        QString serverIP = <span class="string">"xxx.xxx.xxx.xxx"</span>; <span class="comment">// 服务器IP</span></span><br><span class="line">        quint16 serverPort = <span class="number">555</span>;          <span class="comment">// 服务器端口</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 连接信号槽</span></span><br><span class="line">        <span class="built_in">connect</span>(tcpSocket, &amp;QTcpSocket::connected, <span class="keyword">this</span>, [<span class="keyword">this</span>]() {</span><br><span class="line">            <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"TCP: Connected to server"</span>;</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"已连接"</span>);</span><br><span class="line">            ui-&gt;pushbutton_tcp-&gt;<span class="built_in">setText</span>(<span class="string">"断开远程服务器"</span>);</span><br><span class="line">        });</span><br><span class="line"></span><br><span class="line">        <span class="built_in">connect</span>(tcpSocket, &amp;QTcpSocket::disconnected, <span class="keyword">this</span>, [<span class="keyword">this</span>]() {</span><br><span class="line">            <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"TCP: Disconnected from server"</span>;</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"未连接"</span>);</span><br><span class="line">            ui-&gt;pushbutton_tcp-&gt;<span class="built_in">setText</span>(<span class="string">"连接远程服务器"</span>);</span><br><span class="line">            MainWindow::tcp_flag = <span class="literal">false</span>;</span><br><span class="line">        });</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 开始连接</span></span><br><span class="line">        ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"正在连接..."</span>);</span><br><span class="line">        tcpSocket-&gt;<span class="built_in">connectToHost</span>(serverIP, serverPort);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待连接完成（最多等待5秒）</span></span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;<span class="built_in">waitForConnected</span>(<span class="number">5000</span>)) {</span><br><span class="line">            ret = <span class="literal">true</span>;</span><br><span class="line">            <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"TCP: Connection established successfully"</span>;</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"TCP: Connection failed -"</span> &lt;&lt; tcpSocket-&gt;<span class="built_in">errorString</span>();</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"连接失败: "</span> + tcpSocket-&gt;<span class="built_in">errorString</span>());</span><br><span class="line">            ret = <span class="literal">false</span>;</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// 断开连接操作</span></span><br><span class="line">        <span class="keyword">if</span>(tcpSocket-&gt;<span class="built_in">state</span>() == QAbstractSocket::ConnectedState) {</span><br><span class="line">            tcpSocket-&gt;<span class="built_in">disconnectFromHost</span>();</span><br><span class="line">            <span class="keyword">if</span>(tcpSocket-&gt;<span class="built_in">state</span>() == QAbstractSocket::UnconnectedState ||</span><br><span class="line">               tcpSocket-&gt;<span class="built_in">waitForDisconnected</span>(<span class="number">3000</span>)) {</span><br><span class="line">                ret = <span class="literal">true</span>;</span><br><span class="line">                ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"未连接"</span>);</span><br><span class="line">                <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"TCP: Disconnected successfully"</span>;</span><br><span class="line">            }</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            <span class="comment">// 如果当前没有连接，也返回成功</span></span><br><span class="line">            ret = <span class="literal">true</span>;</span><br><span class="line">            ui-&gt;label_tcp_state-&gt;<span class="built_in">setText</span>(<span class="string">"未连接"</span>);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 断开所有信号连接，防止重连的时候无法接收数据</span></span><br><span class="line">        <span class="comment">//tcpSocket-&gt;disconnect();</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">MainWindow::Temperature_Update</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">    <span class="type">bool</span> ret = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用完整的设备路径</span></span><br><span class="line">    QString basePath = <span class="string">"/sys/devices/platform/soc/2100000.aips-bus/2198000.adc/iio:device0"</span>;</span><br><span class="line">    <span class="function">QFile <span class="title">rawFile</span><span class="params">(basePath + <span class="string">"/in_voltage1_raw"</span>)</span></span>;</span><br><span class="line">    <span class="function">QFile <span class="title">scaleFile</span><span class="params">(basePath + <span class="string">"/in_voltage_scale"</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取原始电压值</span></span><br><span class="line">    <span class="keyword">if</span> (!rawFile.<span class="built_in">open</span>(QIODevice::ReadOnly | QIODevice::Text)) {</span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"无法打开原始电压文件:"</span> &lt;&lt; rawFile.<span class="built_in">errorString</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    QString rawStr = <span class="built_in">QTextStream</span>(&amp;rawFile).<span class="built_in">readLine</span>().<span class="built_in">trimmed</span>();</span><br><span class="line">    rawFile.<span class="built_in">close</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取电压缩放比例</span></span><br><span class="line">    <span class="keyword">if</span> (!scaleFile.<span class="built_in">open</span>(QIODevice::ReadOnly | QIODevice::Text)) {</span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"无法打开电压缩放文件:"</span> &lt;&lt; scaleFile.<span class="built_in">errorString</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    QString scaleStr = <span class="built_in">QTextStream</span>(&amp;scaleFile).<span class="built_in">readLine</span>().<span class="built_in">trimmed</span>();</span><br><span class="line">    scaleFile.<span class="built_in">close</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 转换为数值</span></span><br><span class="line">    <span class="type">bool</span> ok1, ok2;</span><br><span class="line">    <span class="type">double</span> rawValue = rawStr.<span class="built_in">toDouble</span>(&amp;ok1);</span><br><span class="line">    <span class="type">double</span> scaleValue = scaleStr.<span class="built_in">toDouble</span>(&amp;ok2);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!ok1 || !ok2) {</span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"数据转换错误: raw="</span> &lt;&lt; rawStr &lt;&lt; <span class="string">"scale="</span> &lt;&lt; scaleStr;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算实际电压值（单位：毫伏 mV）</span></span><br><span class="line">    <span class="type">double</span> voltage_mV = rawValue * scaleValue;</span><br><span class="line">    <span class="type">double</span> voltage_V = voltage_mV / <span class="number">1000.0</span>; <span class="comment">// 转换为伏特 V</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Debug Info - Raw:"</span> &lt;&lt; rawValue &lt;&lt; <span class="string">"Scale:"</span> &lt;&lt; scaleValue &lt;&lt; <span class="string">"Voltage:"</span> &lt;&lt; voltage_mV &lt;&lt; <span class="string">"mV"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//保留更多小数位以便观察变化</span></span><br><span class="line">    QString temperatureText = <span class="built_in">QString</span>(<span class="string">" %1 V"</span>).<span class="built_in">arg</span>(voltage_V, <span class="number">0</span>, <span class="string">'f'</span>, <span class="number">6</span>);</span><br><span class="line">    ui-&gt;label_temp-&gt;<span class="built_in">setText</span>(temperatureText);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function">QString <span class="title">MainWindow::getLocalHostIP</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">    QList&lt;QHostAddress&gt; ipAddressesList = QNetworkInterface::<span class="built_in">allAddresses</span>();</span><br><span class="line">    QString localIP;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历所有IP地址，优先选择IPv4且非回环地址</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; ipAddressesList.<span class="built_in">size</span>(); ++i) {</span><br><span class="line">        QHostAddress ipAddr = ipAddressesList.<span class="built_in">at</span>(i);</span><br><span class="line">        <span class="comment">// 筛选条件：IPv4协议、不是本地回环地址、不是空地址</span></span><br><span class="line">        <span class="keyword">if</span> (ipAddr.<span class="built_in">protocol</span>() == QAbstractSocket::IPv4Protocol &amp;&amp;</span><br><span class="line">            ipAddr != QHostAddress::Null &amp;&amp;</span><br><span class="line">            ipAddr != QHostAddress::LocalHost &amp;&amp;</span><br><span class="line">            !ipAddr.<span class="built_in">toString</span>().<span class="built_in">startsWith</span>(<span class="string">"127.0."</span>) &amp;&amp;</span><br><span class="line">            !ipAddr.<span class="built_in">toString</span>().<span class="built_in">startsWith</span>(<span class="string">"169."</span>)) { <span class="comment">// 排除APIPA地址</span></span><br><span class="line">            localIP = ipAddr.<span class="built_in">toString</span>();</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果没有找到合适的IP，使用本地回环地址作为备选</span></span><br><span class="line">    <span class="keyword">if</span> (localIP.<span class="built_in">isEmpty</span>()) {</span><br><span class="line">        localIP = <span class="string">"127.0.0.1"</span>;</span><br><span class="line">        <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Warning: Using loopback address, no valid IPv4 address found"</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="built_in">qDebug</span>() &lt;&lt; <span class="string">"Local IP Address:"</span> &lt;&lt; localIP;</span><br><span class="line">    <span class="keyword">return</span> localIP;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>              </div>            </details><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>qianduan.html： 点击查看更多 </summary>              <div class="content">              <p>注意修改成自己的tcp和websocket端口号，以及修改服务器地址</p><div class="code-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>IMX6ULL远程控制<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">body</span> { <span class="attribute">font-family</span>: sans-serif; }</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.status</span> { <span class="attribute">padding</span>: <span class="number">10px</span>; <span class="attribute">margin</span>: <span class="number">5px</span>; <span class="attribute">background-color</span>: <span class="number">#f0f0f0</span>; }</span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">button</span> { <span class="attribute">padding</span>: <span class="number">10px</span>; <span class="attribute">margin</span>: <span class="number">5px</span>; }</span></span><br><span class="line"><span class="language-css">        <span class="selector-id">#loginBox</span> { <span class="attribute">max-width</span>: <span class="number">320px</span>; <span class="attribute">margin</span>: <span class="number">80px</span> auto; <span class="attribute">padding</span>: <span class="number">20px</span>; <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ccc</span>; <span class="attribute">border-radius</span>: <span class="number">6px</span>; <span class="attribute">background</span>:<span class="number">#fff</span>; }</span></span><br><span class="line"><span class="language-css">        <span class="selector-id">#loginBox</span> <span class="selector-tag">input</span> { <span class="attribute">width</span>: <span class="number">100%</span>; <span class="attribute">padding</span>: <span class="number">8px</span>; <span class="attribute">margin</span>: <span class="number">6px</span> <span class="number">0</span>; <span class="attribute">box-sizing</span>: border-box; }</span></span><br><span class="line"><span class="language-css">        <span class="selector-id">#mainUI</span> { <span class="attribute">display</span>: none; <span class="attribute">padding</span>: <span class="number">16px</span>; }</span></span><br><span class="line"><span class="language-css">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 登录界面 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"loginBox"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h2</span>&gt;</span>登录<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span>&gt;</span>用户名<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"username"</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">placeholder</span>=<span class="string">"用户名"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span>&gt;</span>密码<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"password"</span> <span class="attr">type</span>=<span class="string">"password"</span> <span class="attr">placeholder</span>=<span class="string">"密码"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">"text-align:right;"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"doLogin()"</span>&gt;</span>登录<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 主界面 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"mainUI"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h1</span>&gt;</span>IMX6ULL智能家居控制台<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"connectionStatus"</span> <span class="attr">class</span>=<span class="string">"status"</span>&gt;</span>未连接<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"sendCommand('LED1_ON')"</span>&gt;</span>打开LED<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"sendCommand('LED1_OFF')"</span>&gt;</span>关闭LED<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"sendCommand('GET_TEMP')"</span>&gt;</span>获取温度<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"sendCommand('BEEP_ON')"</span>&gt;</span>打开蜂鸣器<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"sendCommand('BEEP_OFF')"</span>&gt;</span>关闭蜂鸣器<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"logout()"</span> <span class="attr">style</span>=<span class="string">"float:right;"</span>&gt;</span>登出<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"sensorData"</span> <span class="attr">style</span>=<span class="string">"margin-top:12px;"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">const</span> serverIP = <span class="string">'xx.xxx.xxx.xxx'</span>;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">const</span> wsPort = <span class="number">555</span>;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">let</span> websocket = <span class="literal">null</span>;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">let</span> deviceConnected = <span class="literal">false</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">markControlButtons</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> btns = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">'#mainUI button'</span>);</span></span><br><span class="line"><span class="language-javascript">            btns.<span class="title function_">forEach</span>(<span class="function"><span class="params">b</span> =&gt;</span> {</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">if</span> (b.<span class="title function_">getAttribute</span>(<span class="string">'onclick'</span>) &amp;&amp; b.<span class="title function_">getAttribute</span>(<span class="string">'onclick'</span>).<span class="title function_">includes</span>(<span class="string">'sendCommand'</span>)) {</span></span><br><span class="line"><span class="language-javascript">                    b.<span class="property">dataset</span>.<span class="property">control</span> = <span class="string">'1'</span>;</span></span><br><span class="line"><span class="language-javascript">                }</span></span><br><span class="line"><span class="language-javascript">            });</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">setDeviceConnected</span>(<span class="params">connected</span>) {</span></span><br><span class="line"><span class="language-javascript">            deviceConnected = connected;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> status = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'connectionStatus'</span>);</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (connected) {</span></span><br><span class="line"><span class="language-javascript">                status.<span class="property">innerHTML</span> = <span class="string">"设备已连接"</span>;</span></span><br><span class="line"><span class="language-javascript">                status.<span class="property">style</span>.<span class="property">backgroundColor</span> = <span class="string">'#90EE90'</span>;</span></span><br><span class="line"><span class="language-javascript">            } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript">                status.<span class="property">innerHTML</span> = <span class="string">"设备未连接"</span>;</span></span><br><span class="line"><span class="language-javascript">                status.<span class="property">style</span>.<span class="property">backgroundColor</span> = <span class="string">'#FFB6C1'</span>;</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">'#mainUI button[data-control="1"]'</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">b</span> =&gt;</span> {</span></span><br><span class="line"><span class="language-javascript">                b.<span class="property">disabled</span> = !connected;</span></span><br><span class="line"><span class="language-javascript">            });</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">showMainUI</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'loginBox'</span>).<span class="property">style</span>.<span class="property">display</span> = <span class="string">'none'</span>;</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'mainUI'</span>).<span class="property">style</span>.<span class="property">display</span> = <span class="string">'block'</span>;</span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">markControlButtons</span>();</span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">setDeviceConnected</span>(<span class="literal">false</span>); <span class="comment">// 等后端通知 DEVICE_CONNECTED 再启用</span></span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">connect</span>();</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">doLogin</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> user = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'username'</span>).<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> pass = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'password'</span>).<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (user === <span class="string">'xxxxxxx'</span> &amp;&amp; pass === <span class="string">'xxxxxxx'</span>) {</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">sessionStorage</span>.<span class="title function_">setItem</span>(<span class="string">'loggedIn'</span>, <span class="string">'1'</span>);</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">showMainUI</span>();</span></span><br><span class="line"><span class="language-javascript">            } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">alert</span>(<span class="string">'用户名或密码错误'</span>);</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">logout</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">sessionStorage</span>.<span class="title function_">removeItem</span>(<span class="string">'loggedIn'</span>);</span></span><br><span class="line"><span class="language-javascript">            location.<span class="title function_">reload</span>();</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 辅助：把 hex 字符串转换为 UTF-8 字符串并返回</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">hexToString</span>(<span class="params">hex</span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 移除可能的空格或 0x 前缀</span></span></span><br><span class="line"><span class="language-javascript">            hex = hex.<span class="title function_">replace</span>(<span class="regexp">/\s+/g</span>, <span class="string">''</span>).<span class="title function_">replace</span>(<span class="regexp">/0x/g</span>,<span class="string">''</span>)</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (hex.<span class="property">length</span> === <span class="number">0</span>) <span class="keyword">return</span> <span class="string">''</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 若奇数位，前面补 0</span></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (hex.<span class="property">length</span> % <span class="number">2</span> !== <span class="number">0</span>) {</span></span><br><span class="line"><span class="language-javascript">                hex = <span class="string">'0'</span> + hex;</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">try</span> {</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">const</span> bytes = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(hex.<span class="property">length</span> / <span class="number">2</span>);</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; hex.<span class="property">length</span>; i += <span class="number">2</span>) {</span></span><br><span class="line"><span class="language-javascript">                    bytes[i / <span class="number">2</span>] = <span class="built_in">parseInt</span>(hex.<span class="title function_">substr</span>(i, <span class="number">2</span>), <span class="number">16</span>);</span></span><br><span class="line"><span class="language-javascript">                }</span></span><br><span class="line"><span class="language-javascript">                <span class="comment">// 使用 TextDecoder 解码为字符串（不可解的字节会被替代字符替换）</span></span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">const</span> dec = <span class="keyword">new</span> <span class="title class_">TextDecoder</span>(<span class="string">'utf-8'</span>);</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">return</span> dec.<span class="title function_">decode</span>(bytes);</span></span><br><span class="line"><span class="language-javascript">            } <span class="keyword">catch</span> (e) {</span></span><br><span class="line"><span class="language-javascript">                <span class="comment">// 任何异常直接返回原 hex（但你说不要 hex，仍尽量返回空串）</span></span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">'hexToString decode failed'</span>, e);</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">return</span> <span class="string">''</span>;</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">connect</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">            websocket = <span class="keyword">new</span> <span class="title class_">WebSocket</span>(<span class="string">`ws://<span class="subst">${serverIP}</span>:<span class="subst">${wsPort}</span>`</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            websocket.<span class="property">onopen</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'connectionStatus'</span>).<span class="property">innerHTML</span> = <span class="string">"已连接到服务器（等待设备状态）"</span>;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'connectionStatus'</span>).<span class="property">style</span>.<span class="property">backgroundColor</span> = <span class="string">'#FFFF99'</span>;</span></span><br><span class="line"><span class="language-javascript">            };</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            websocket.<span class="property">onmessage</span> = <span class="keyword">function</span> (<span class="params">event</span>) {</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">const</span> msg = event.<span class="property">data</span>;</span></span><br><span class="line"><span class="language-javascript">                <span class="comment">// 后端发的是文本控制消息：</span></span></span><br><span class="line"><span class="language-javascript">                <span class="comment">// DEVICE_CONNECTED / DEVICE_DISCONNECTED / DATA:hex / TEMP:val / ERROR:...</span></span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">if</span> (<span class="keyword">typeof</span> msg === <span class="string">'string'</span>) {</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">if</span> (msg === <span class="string">"DEVICE_CONNECTED"</span>) {</span></span><br><span class="line"><span class="language-javascript">                        <span class="title function_">setDeviceConnected</span>(<span class="literal">true</span>);</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">                    }</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">if</span> (msg === <span class="string">"DEVICE_DISCONNECTED"</span>) {</span></span><br><span class="line"><span class="language-javascript">                        <span class="title function_">setDeviceConnected</span>(<span class="literal">false</span>);</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">                    }</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">if</span> (msg.<span class="title function_">startsWith</span>(<span class="string">"TEMP:"</span>)) {</span></span><br><span class="line"><span class="language-javascript">                        <span class="comment">// TEMP:0.98  直接显示电压值字符串</span></span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">const</span> val = msg.<span class="title function_">slice</span>(<span class="number">5</span>);</span></span><br><span class="line"><span class="language-javascript">                        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'sensorData'</span>).<span class="property">innerHTML</span> = <span class="string">`电压: <span class="subst">${val}</span> V`</span>;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">                    }</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">if</span> (msg.<span class="title function_">startsWith</span>(<span class="string">"DATA:"</span>)) {</span></span><br><span class="line"><span class="language-javascript">                        <span class="comment">// DATA:hexstring  —— 尝试把 hex 解码为字符串并直接显示</span></span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">const</span> hex = msg.<span class="title function_">slice</span>(<span class="number">5</span>);</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">const</span> text = <span class="title function_">hexToString</span>(hex);</span></span><br><span class="line"><span class="language-javascript">                        <span class="comment">// 若解码后为空（不可解），显示“收到不可读数据”</span></span></span><br><span class="line"><span class="language-javascript">                        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'sensorData'</span>).<span class="property">innerHTML</span> = text !== <span class="string">''</span> ? <span class="string">`收到设备字符串：<span class="subst">${text}</span>`</span> : <span class="string">`收到不可读二进制数据（无法解码为文本）`</span>;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">                    }</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">if</span> (msg.<span class="title function_">startsWith</span>(<span class="string">"ERROR:"</span>)) {</span></span><br><span class="line"><span class="language-javascript">                        <span class="title function_">alert</span>(msg);</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">                    }</span></span><br><span class="line"><span class="language-javascript">                    <span class="comment">// 其它文本消息直接显示</span></span></span><br><span class="line"><span class="language-javascript">                    <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'sensorData'</span>).<span class="property">innerHTML</span> = <span class="string">`收到: <span class="subst">${msg}</span>`</span>;</span></span><br><span class="line"><span class="language-javascript">                } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript">                    <span class="comment">// 若收到了二进制（后端目前不发二进制），直接把二进制用 TextDecoder 解码并显示</span></span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">try</span> {</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">const</span> reader = <span class="keyword">new</span> <span class="title class_">FileReader</span>();</span></span><br><span class="line"><span class="language-javascript">                        reader.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">const</span> arrayBuffer = reader.<span class="property">result</span>;</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">const</span> bytes = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(arrayBuffer);</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">const</span> dec = <span class="keyword">new</span> <span class="title class_">TextDecoder</span>(<span class="string">'utf-8'</span>);</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">const</span> s = dec.<span class="title function_">decode</span>(bytes);</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'sensorData'</span>).<span class="property">innerHTML</span> = <span class="string">`收到设备字符串（二进制帧）：<span class="subst">${s}</span>`</span>;</span></span><br><span class="line"><span class="language-javascript">                        };</span></span><br><span class="line"><span class="language-javascript">                        reader.<span class="title function_">readAsArrayBuffer</span>(event.<span class="property">data</span>);</span></span><br><span class="line"><span class="language-javascript">                    } <span class="keyword">catch</span> (e) {</span></span><br><span class="line"><span class="language-javascript">                        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'sensorData'</span>).<span class="property">innerHTML</span> = <span class="string">`收到二进制消息（len=<span class="subst">${event.data.byteLength}</span>)`</span>;</span></span><br><span class="line"><span class="language-javascript">                    }</span></span><br><span class="line"><span class="language-javascript">                }</span></span><br><span class="line"><span class="language-javascript">            };</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            websocket.<span class="property">onclose</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'connectionStatus'</span>).<span class="property">innerHTML</span> = <span class="string">"连接已断开（与服务器）"</span>;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'connectionStatus'</span>).<span class="property">style</span>.<span class="property">backgroundColor</span> = <span class="string">'#FFB6C1'</span>;</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">setDeviceConnected</span>(<span class="literal">false</span>);</span></span><br><span class="line"><span class="language-javascript">            };</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            websocket.<span class="property">onerror</span> = <span class="keyword">function</span> (<span class="params">e</span>) {</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'WebSocket error'</span>, e);</span></span><br><span class="line"><span class="language-javascript">            };</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// sendCommand 支持二进制发送（浏览器）或文本回退</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">sendBinaryArray</span>(<span class="params">arr</span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (websocket &amp;&amp; websocket.<span class="property">readyState</span> === <span class="title class_">WebSocket</span>.<span class="property">OPEN</span>) {</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">try</span> {</span></span><br><span class="line"><span class="language-javascript">                    websocket.<span class="title function_">send</span>(<span class="keyword">new</span> <span class="title class_">Uint8Array</span>(arr));</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">return</span> <span class="literal">true</span>;</span></span><br><span class="line"><span class="language-javascript">                } <span class="keyword">catch</span> (e) {</span></span><br><span class="line"><span class="language-javascript">                    <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">"Binary send failed, will try textual fallback"</span>, e);</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">return</span> <span class="literal">false</span>;</span></span><br><span class="line"><span class="language-javascript">                }</span></span><br><span class="line"><span class="language-javascript">            } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">alert</span>(<span class="string">"未连接到服务器！"</span>);</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">return</span> <span class="literal">false</span>;</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">sendCommand</span>(<span class="params">cmd</span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (!deviceConnected) {</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">alert</span>(<span class="string">"设备未连接，无法发送命令"</span>);</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 映射命令到三字节包</span></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> map = {</span></span><br><span class="line"><span class="language-javascript">                <span class="string">"LED1_ON"</span>: [<span class="number">0x01</span>, <span class="number">0x01</span>, <span class="number">0xFF</span>],</span></span><br><span class="line"><span class="language-javascript">                <span class="string">"LED1_OFF"</span>: [<span class="number">0x01</span>, <span class="number">0x02</span>, <span class="number">0xFF</span>],</span></span><br><span class="line"><span class="language-javascript">                <span class="string">"BEEP_ON"</span>: [<span class="number">0x02</span>, <span class="number">0x01</span>, <span class="number">0xFF</span>],</span></span><br><span class="line"><span class="language-javascript">                <span class="string">"BEEP_OFF"</span>: [<span class="number">0x02</span>, <span class="number">0x02</span>, <span class="number">0xFF</span>],</span></span><br><span class="line"><span class="language-javascript">                <span class="string">"GET_TEMP"</span>: [<span class="number">0x03</span>, <span class="number">0x01</span>, <span class="number">0xFF</span>],</span></span><br><span class="line"><span class="language-javascript">            };</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> payload = map[cmd];</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (!payload) {</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">alert</span>(<span class="string">"未知命令"</span>);</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">return</span>;</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 先尝试二进制发送（浏览器支持），否则回退为字符串命令</span></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> ok = <span class="title function_">sendBinaryArray</span>(payload);</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (!ok) {</span></span><br><span class="line"><span class="language-javascript">                websocket.<span class="title function_">send</span>(cmd);</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript">        }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">window</span>.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (<span class="variable language_">sessionStorage</span>.<span class="title function_">getItem</span>(<span class="string">'loggedIn'</span>) === <span class="string">'1'</span>) {</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">showMainUI</span>();</span></span><br><span class="line"><span class="language-javascript">            } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'username'</span>).<span class="title function_">focus</span>();</span></span><br><span class="line"><span class="language-javascript">            }</span></span><br><span class="line"><span class="language-javascript">        };</span></span><br><span class="line"><span class="language-javascript">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure></div>              </div>            </details><details class="blue" data-header-exclude=""><summary><i class="fa-solid fa-chevron-right"></i>houduan.py： 点击查看更多 </summary>              <div class="content">              <p>注意修改成自己的tcp端口号</p><div class="code-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> websockets</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="comment"># 存储当前设备的 StreamWriter（假设只有一台 imx6ull）</span></span><br><span class="line">device_writer = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 存储当前连接的网页客户端（websocket 实例）</span></span><br><span class="line">web_clients = <span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 广播文本消息给所有网页客户端（非阻塞）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">broadcast_text</span>(<span class="params">msg: <span class="built_in">str</span></span>):</span><br><span class="line">    <span class="keyword">for</span> ws <span class="keyword">in</span> <span class="built_in">list</span>(web_clients):</span><br><span class="line">        <span class="comment"># 创建任务异步发送，发送失败时移除该客户端</span></span><br><span class="line">        asyncio.create_task(_safe_send(ws, msg))</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">_safe_send</span>(<span class="params">ws, msg: <span class="built_in">str</span></span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">await</span> ws.send(msg)</span><br><span class="line">    <span class="keyword">except</span> Exception:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">await</span> ws.close()</span><br><span class="line">        <span class="keyword">except</span> Exception:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">        web_clients.discard(ws)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">handle_imx6ull</span>(<span class="params">reader: asyncio.StreamReader, writer: asyncio.StreamWriter</span>):</span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    处理 imx6ull 的 TCP 连接。</span></span><br><span class="line"><span class="string">    - 把收到的原始字节尝试按 UTF-8 解码；如果是数字/浮点文本（例如 "0.123"），当作 TEMP 广播；</span></span><br><span class="line"><span class="string">      否则以 hex 广播为 DATA。</span></span><br><span class="line"><span class="string">    - 当设备连接/断开时广播 DEVICE_CONNECTED / DEVICE_DISCONNECTED</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    <span class="keyword">global</span> device_writer</span><br><span class="line">    peer = writer.get_extra_info(<span class="string">'peername'</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f"[TCP] imx6ull connected from <span class="subst">{peer}</span>"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 如果已有旧的 writer，先关闭它（简单处理）</span></span><br><span class="line">    <span class="keyword">if</span> device_writer <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> device_writer <span class="keyword">is</span> <span class="keyword">not</span> writer:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            device_writer.close()</span><br><span class="line">            <span class="keyword">await</span> device_writer.wait_closed()</span><br><span class="line">        <span class="keyword">except</span> Exception:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    device_writer = writer</span><br><span class="line">    broadcast_text(<span class="string">"DEVICE_CONNECTED"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            data = <span class="keyword">await</span> reader.read(<span class="number">1024</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> data:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">"[TCP] imx6ull closed connection"</span>)</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 先尝试把 bytes 解码为 utf-8 文本（去掉首尾空白）</span></span><br><span class="line">            decoded = <span class="literal">None</span></span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                decoded = data.decode(<span class="string">'utf-8'</span>, errors=<span class="string">'strict'</span>).strip()</span><br><span class="line">            <span class="keyword">except</span> Exception:</span><br><span class="line">                decoded = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 如果 decode 成功并且看起来像数字或浮点（比如 "0.123"、"3.30"、"1"），当作 TEMP 广播</span></span><br><span class="line">            <span class="keyword">if</span> decoded:</span><br><span class="line">                <span class="comment"># 简单的匹配：整数字或浮点（可带小数点）</span></span><br><span class="line">                <span class="keyword">if</span> re.fullmatch(<span class="string">r'\d+(\.\d+)?'</span>, decoded):</span><br><span class="line">                    <span class="comment"># 前端直接显示字符串（例如 "0.98"）</span></span><br><span class="line">                    broadcast_text(<span class="string">"TEMP:"</span> + decoded)</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f"[TCP] recv TEMP <span class="subst">{decoded}</span> from device"</span>)</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 否则按 hex 广播（便于调试二进制）</span></span><br><span class="line">            hexstr = data.<span class="built_in">hex</span>()</span><br><span class="line">            broadcast_text(<span class="string">"DATA:"</span> + hexstr)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f"[TCP] recv hex <span class="subst">{hexstr}</span> from device"</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f"[TCP] device error: <span class="subst">{e}</span>"</span>)</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        <span class="comment"># 清理</span></span><br><span class="line">        <span class="keyword">if</span> device_writer <span class="keyword">is</span> writer:</span><br><span class="line">            device_writer = <span class="literal">None</span></span><br><span class="line">        broadcast_text(<span class="string">"DEVICE_DISCONNECTED"</span>)</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            writer.close()</span><br><span class="line">            <span class="keyword">await</span> writer.wait_closed()</span><br><span class="line">        <span class="keyword">except</span> Exception:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">"[TCP] imx6ull cleanup done"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">handle_web_client</span>(<span class="params">ws, path</span>):</span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    处理网页的 websocket 连接。</span></span><br><span class="line"><span class="string">    - 接收二进制消息或文本命令并转发到 device_writer（TCP）</span></span><br><span class="line"><span class="string">    - 初次连接时通知当前设备状态</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    <span class="keyword">global</span> device_writer</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">"[WS] web client connected"</span>)</span><br><span class="line">    web_clients.add(ws)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 发送当前设备状态到该客户端</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">if</span> device_writer <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">await</span> ws.send(<span class="string">"DEVICE_CONNECTED"</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">await</span> ws.send(<span class="string">"DEVICE_DISCONNECTED"</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">for</span> msg <span class="keyword">in</span> ws:</span><br><span class="line">            <span class="comment"># msg 可能是 str（文本帧）或 bytes（二进制帧）</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">isinstance</span>(msg, <span class="built_in">bytes</span>):</span><br><span class="line">                <span class="comment"># 直接把二进制数据原样转发给设备</span></span><br><span class="line">                <span class="keyword">if</span> device_writer <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">                    <span class="keyword">await</span> _safe_send(ws, <span class="string">"ERROR: device not connected"</span>)</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">                transport = <span class="built_in">getattr</span>(device_writer, <span class="string">"transport"</span>, <span class="literal">None</span>)</span><br><span class="line">                <span class="keyword">if</span> transport <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> transport.is_closing():</span><br><span class="line">                    <span class="comment"># 设备看起来已断开，清理并通知所有网页</span></span><br><span class="line">                    device_writer = <span class="literal">None</span></span><br><span class="line">                    broadcast_text(<span class="string">"DEVICE_DISCONNECTED"</span>)</span><br><span class="line">                    <span class="keyword">await</span> _safe_send(ws, <span class="string">"ERROR: device disconnected"</span>)</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    device_writer.write(msg)  <span class="comment"># 原样写入</span></span><br><span class="line">                    <span class="keyword">await</span> device_writer.drain()</span><br><span class="line">                <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f"[WS-&gt;TCP] send error: <span class="subst">{e}</span>"</span>)</span><br><span class="line">                    device_writer = <span class="literal">None</span></span><br><span class="line">                    broadcast_text(<span class="string">"DEVICE_DISCONNECTED"</span>)</span><br><span class="line">                    <span class="keyword">await</span> _safe_send(ws, <span class="string">"ERROR: failed to send to device"</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="comment"># 兼容旧文本命令（如果前端仍发送字符串），这里简单映射为指定的三字节包</span></span><br><span class="line">                cmd = <span class="built_in">str</span>(msg)</span><br><span class="line">                mapping = {</span><br><span class="line">                    <span class="string">"LED1_ON"</span>: <span class="built_in">bytes</span>([<span class="number">0x01</span>, <span class="number">0x01</span>, <span class="number">0xFF</span>]),</span><br><span class="line">                    <span class="string">"LED1_OFF"</span>: <span class="built_in">bytes</span>([<span class="number">0x01</span>, <span class="number">0x02</span>, <span class="number">0xFF</span>]),</span><br><span class="line">                    <span class="string">"BEEP_ON"</span>: <span class="built_in">bytes</span>([<span class="number">0x02</span>, <span class="number">0x01</span>, <span class="number">0xFF</span>]),</span><br><span class="line">                    <span class="string">"BEEP_OFF"</span>: <span class="built_in">bytes</span>([<span class="number">0x02</span>, <span class="number">0x02</span>, <span class="number">0xFF</span>]),</span><br><span class="line">                    <span class="string">"GET_TEMP"</span>: <span class="built_in">bytes</span>([<span class="number">0x03</span>, <span class="number">0x01</span>, <span class="number">0xFF</span>]),</span><br><span class="line">                }</span><br><span class="line">                payload = mapping.get(cmd)</span><br><span class="line">                <span class="keyword">if</span> payload:</span><br><span class="line">                    <span class="keyword">if</span> device_writer <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">                        <span class="keyword">await</span> _safe_send(ws, <span class="string">"ERROR: device not connected"</span>)</span><br><span class="line">                        <span class="keyword">continue</span></span><br><span class="line">                    <span class="keyword">try</span>:</span><br><span class="line">                        device_writer.write(payload)</span><br><span class="line">                        <span class="keyword">await</span> device_writer.drain()</span><br><span class="line">                    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">f"[WS-&gt;TCP] send error: <span class="subst">{e}</span>"</span>)</span><br><span class="line">                        device_writer = <span class="literal">None</span></span><br><span class="line">                        broadcast_text(<span class="string">"DEVICE_DISCONNECTED"</span>)</span><br><span class="line">                        <span class="keyword">await</span> _safe_send(ws, <span class="string">"ERROR: failed to send to device"</span>)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    <span class="comment"># 未知文本，直接回显或忽略</span></span><br><span class="line">                    <span class="keyword">await</span> _safe_send(ws, <span class="string">"ERROR: unknown command"</span>)</span><br><span class="line">    <span class="keyword">except</span> websockets.exceptions.ConnectionClosed:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f"[WS] client error: <span class="subst">{e}</span>"</span>)</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        web_clients.discard(ws)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">"[WS] web client disconnected"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 启动 TCP server（imx6ull） 444是tcp端口号,记得改成自己的端口号,555是websocket端口号</span></span><br><span class="line">    tcp_server = <span class="keyword">await</span> asyncio.start_server(handle_imx6ull, <span class="string">"0.0.0.0"</span>, <span class="number">444</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">"[MAIN] TCP server listening on 0.0.0.0:444"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 启动 WebSocket server（网页）</span></span><br><span class="line">    ws_server = <span class="keyword">await</span> websockets.serve(handle_web_client, <span class="string">"0.0.0.0"</span>, <span class="number">555</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">"[MAIN] WebSocket server listening on 0.0.0.0:555"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> asyncio.gather(tcp_server.serve_forever(), ws_server.wait_closed())</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line">    asyncio.run(main())</span><br></pre></td></tr></table></figure></div>              </div>            </details><h2 id="G-拓展部分-Linux智能车载控制系统"><a href="#G-拓展部分-Linux智能车载控制系统" class="headerlink" title="G 拓展部分-Linux智能车载控制系统"></a>G 拓展部分-Linux智能车载控制系统</h2><p>智能家居可以让我们大致体验到基础功能，但是作为一个实际的系统，不可能功能这么少/简略，如果还是在智能家居方面，其实主要添加点在于控制家居，也就是添加继电器或者其它模块，或者是一些物理网的协议能实现对家居的控制，而不是仅仅读取传感器</p><p>所以这里引入Linux智能车载控制系统部分，这里的外设还是<strong>蜂鸣器/LED</strong>，然后同样是<strong>温湿度/板载的光照传感器</strong>，在调试方面可以选择使用<strong>串口输出/蓝牙输出</strong>，然后结合QT的<strong>多媒体功能</strong>和<strong>TCP网络</strong>的方面外部接口(实现比如<strong>天气/时间等的获取</strong>)，当来实现一个相对高级的车载系统</p><p>下面还是先介绍一下需要使用的东西，当然，是在智能家居的环境以及底层驱动的基础上添加/修改布局，对于上层，我们主要添加定时器定时对天气的更新(保留对传感器的定时读取)</p><h3 id="G-1-多媒体部分"><a href="#G-1-多媒体部分" class="headerlink" title="G.1 多媒体部分"></a>G.1 多媒体部分</h3><p>多媒体包括 音频 和 视频，但是可惜的是我的IMX6ull是 mini版本，所以并没有音频的模块，所以这里也只能实现视频部分，音频部分依旧使用蜂鸣器进行替代，当然，如果想播放音乐，可以选择参考正点原子的QT指南的多媒体模块学习</p><p>/待补充…………………………………</p><h4 id="G-1-1-准备环境"><a href="#G-1-1-准备环境" class="headerlink" title="G.1.1 准备环境"></a>G.1.1 准备环境</h4><h4 id="G-1-2-相关库-函数的介绍"><a href="#G-1-2-相关库-函数的介绍" class="headerlink" title="G 1.2 相关库/函数的介绍"></a>G 1.2 相关库/函数的介绍</h4><h4 id="G-1-3-如何添加资源-补充"><a href="#G-1-3-如何添加资源-补充" class="headerlink" title="G 1.3 如何添加资源(补充)"></a>G 1.3 如何添加资源(补充)</h4><h4 id="G-1-4-代码的具体实现"><a href="#G-1-4-代码的具体实现" class="headerlink" title="G 1.4 代码的具体实现"></a>G 1.4 代码的具体实现</h4><p>最后这里还要考虑在视频播放的时候，如何返回主界面,所以要对界面进行一个设计</p><h3 id="G-2-界面设计-逻辑部分"><a href="#G-2-界面设计-逻辑部分" class="headerlink" title="G.2 界面设计/逻辑部分"></a>G.2 界面设计/逻辑部分</h3><h3 id="G-3-获取天气-网络部分"><a href="#G-3-获取天气-网络部分" class="headerlink" title="G.3 获取天气/网络部分"></a>G.3 获取天气/网络部分</h3><h3 id="G-4-整体逻辑链接"><a href="#G-4-整体逻辑链接" class="headerlink" title="G.4 整体逻辑链接"></a>G.4 整体逻辑链接</h3><h3 id="G-5-源码"><a href="#G-5-源码" class="headerlink" title="G.5 源码"></a>G.5 源码</h3><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><a class="link" href="http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html">【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#51;&#x31;&#52;&#54;&#x37;&#48;&#x32;&#51;&#54;&#50;&#64;&#113;&#113;&#46;&#99;&#111;&#x6d;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">在学习imx6ull的时候，同时想巩固一下QT，IMX6u驱动，利用自己的云服务器，结合做一个简易的智能家居项目，这里主要记录一下项目具体内容以及遇到的问题与解决办法，同时在最后添加这个项目可以添加什么可以形成更有价值的项目，比如Linux智能车载控制系统等...</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/Embedded/Linux/"/>
    
    <category term="Qt" scheme="https://blog.haozi-haozi.cn/categories/Embedded/Linux/Qt/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="Qt" scheme="https://blog.haozi-haozi.cn/tags/Qt/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="i.MX6ULL" scheme="https://blog.haozi-haozi.cn/tags/i-MX6ULL/"/>
    
  </entry>
  
  <entry>
    <title>基于 i.MX6ULL-Linux的触摸屏(GT911)驱动</title>
    <link href="https://blog.haozi-haozi.cn/2025/09/26/embedded_imx6ull_gt911/"/>
    <id>https://blog.haozi-haozi.cn/2025/09/26/embedded_imx6ull_gt911/</id>
    <published>2025-09-26T05:22:14.000Z</published>
    <updated>2026-03-24T08:57:51.181Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A 前情提要"></a>A 前情提要</h2><p>这里需要用到的前置知识有: <strong>Linux驱动基础，IIC子系统，INPUT子系统，Pinctrl和GPIO子系统，以及中断驱动</strong><br>其中 IIC 用来读取触摸屏的信息，INPUT用来向linux内核上报信息，当屏幕被按下触发中断</p><h2 id="B-GT911-大致介绍"><a href="#B-GT911-大致介绍" class="headerlink" title="B GT911 大致介绍"></a>B GT911 大致介绍</h2><p>GT911、GT928、GT9147都属于GT9系列非单层多点触控芯片，他们支持的触控点数不同（GT928支持10个点、GT911支持5个点）、驱动和感应通道也可能不同。可是他们的寄存器和IIC通讯时序是相同的，也就是说驱动程序是基本兼容的。</p><p>GT911初始化的时候有<strong>两种方法选择IIC地址</strong></p><ol><li><p>把RST、INT拉低，延时10ms，把INT拉高，延时100us，把RST拉高，就可以把IIC地址设为0x28/0x29，<strong>七位地址就是 0X14</strong></p></li><li><p>把RST、INT拉低，延时10ms，把RST拉高，就可以把IIC地址设为0xBA/0xBB <strong>七位地址就是 0X5D</strong></p></li></ol><p>下面我们选择第二种方法来初始化屏幕，设置完地址之后就需要<strong>把INT设置为悬空输出模式</strong>，RST保持拉高，<strong>默认通过 上升沿 触发中断，然后来读取触摸信息</strong></p><h2 id="C-设备树修改"><a href="#C-设备树修改" class="headerlink" title="C 设备树修改"></a>C 设备树修改</h2><p>主要是设置复位引脚，中断引脚和IIC引脚，下面给出修改部分，同时要<strong>注意屏蔽掉使用这四个引脚的其它驱动，防止其它驱动操作了这四个引脚发生错误</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**************reset引脚部分****************/</span></span><br><span class="line">&amp;iomuxc_snvs {</span><br><span class="line"> pinctrl-names = <span class="string">"default_snvs"</span>;</span><br><span class="line">        pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_hog_2&gt;;</span><br><span class="line">        imx6ul-evk {</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">//Mouse MT ret </span></span><br><span class="line">  pinctrl_tsc_reset: tsc_reset{</span><br><span class="line">        fsl,pins = &lt;</span><br><span class="line">              MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09     <span class="number">0x10B0</span> </span><br><span class="line">          &gt;;</span><br><span class="line">        };</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**************int引脚部分****************/</span></span><br><span class="line"></span><br><span class="line">&amp;iomuxc {</span><br><span class="line"> pinctrl-names = <span class="string">"default"</span>;</span><br><span class="line"> pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_hog_1&gt;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">//Mouse INT </span></span><br><span class="line">  pinctrl_tsc: tscgrp{</span><br><span class="line">   fsl,pins = &lt;</span><br><span class="line">    MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 <span class="number">0x79</span></span><br><span class="line">   &gt;;</span><br><span class="line">  };</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**************IIC引脚部分****************/</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&amp;iomuxc {</span><br><span class="line"> pinctrl-names = <span class="string">"default"</span>;</span><br><span class="line"> pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_hog_1&gt;;</span><br><span class="line"> </span><br><span class="line"> imx6ul-evk {</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line"></span><br><span class="line">  pinctrl_i2c2: i2c2grp {</span><br><span class="line">   fsl,pins = &lt;</span><br><span class="line">    MX6UL_PAD_UART5_TX_DATA__I2C2_SCL <span class="number">0x4001b8b0</span></span><br><span class="line">    MX6UL_PAD_UART5_RX_DATA__I2C2_SDA <span class="number">0x4001b8b0</span></span><br><span class="line">   &gt;;</span><br><span class="line">  };</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line"></span><br><span class="line">  }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**************GPIO节点部分****************/</span></span><br><span class="line"></span><br><span class="line">&amp;i2c2 {</span><br><span class="line"> clock_frequency = &lt;<span class="number">100000</span>&gt;;</span><br><span class="line"> pinctrl-names = <span class="string">"default"</span>;</span><br><span class="line"> pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_i2c2&gt;;</span><br><span class="line"> status = <span class="string">"okay"</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line"></span><br><span class="line">gt911:gt911@<span class="number">5</span>d {</span><br><span class="line">  compatible = <span class="string">"goodix,gt911"</span>;</span><br><span class="line">  reg = &lt;<span class="number">0x5d</span>&gt;;</span><br><span class="line">  pinctrl-names = <span class="string">"default"</span>;</span><br><span class="line">  pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_tsc </span><br><span class="line">      &amp;pinctrl_tsc_reset &gt;;</span><br><span class="line">  interrupt-parent = &lt;&amp;gpio1&gt;;</span><br><span class="line">  interrupts = &lt;<span class="number">9</span> <span class="number">1</span>&gt;;  <span class="comment">/* 使用的是9号中断 因为引脚是9  触发方式是1 上升沿触发 */</span></span><br><span class="line">  reset-gpios  = &lt;&amp;gpio5 <span class="number">9</span> GPIO_ACTIVE_LOW&gt;;  <span class="comment">/* 4*32 + 9 = 128+9 = 137 */</span></span><br><span class="line">  interrupt-gpios = &lt;&amp;gpio1 <span class="number">9</span> GPIO_ACTIVE_LOW&gt;; <span class="comment">/* 9 */</span></span><br><span class="line">  goodix,cfg-group0 = [</span><br><span class="line">   <span class="number">41</span> <span class="number">20</span> <span class="number">03</span> E0 <span class="number">01</span> <span class="number">05</span> BD <span class="number">00</span> <span class="number">01</span> <span class="number">0F</span> </span><br><span class="line">   <span class="number">14</span> <span class="number">0F</span> <span class="number">5F</span> <span class="number">32</span> <span class="number">03</span> <span class="number">05</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">18</span> <span class="number">1</span>A <span class="number">1</span>C <span class="number">14</span> <span class="number">89</span> <span class="number">29</span> <span class="number">0B</span> </span><br><span class="line">   <span class="number">3</span>A <span class="number">38</span> <span class="number">8F</span> <span class="number">04</span> <span class="number">00</span> <span class="number">00</span> <span class="number">01</span> <span class="number">21</span> <span class="number">02</span> <span class="number">1</span>D </span><br><span class="line">   <span class="number">00</span> <span class="number">01</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">28</span> <span class="number">50</span> <span class="number">94</span> C5 <span class="number">02</span> <span class="number">08</span> <span class="number">00</span> <span class="number">00</span> <span class="number">04</span> </span><br><span class="line">   <span class="number">80</span> <span class="number">2</span>A <span class="number">00</span> <span class="number">80</span> <span class="number">31</span> <span class="number">00</span> <span class="number">83</span> <span class="number">38</span> <span class="number">00</span> <span class="number">8</span>C </span><br><span class="line">   <span class="number">41</span> <span class="number">00</span> <span class="number">9B</span> <span class="number">4</span>A <span class="number">00</span> <span class="number">9</span>A <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">16</span> <span class="number">14</span> <span class="number">12</span> <span class="number">10</span> <span class="number">0</span>E <span class="number">0</span>C <span class="number">0</span>A <span class="number">08</span> </span><br><span class="line">   <span class="number">06</span> <span class="number">04</span> <span class="number">02</span> FF FF FF <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">02</span> <span class="number">04</span> <span class="number">06</span> <span class="number">08</span> <span class="number">0</span>A <span class="number">0F</span> <span class="number">10</span> </span><br><span class="line">   <span class="number">12</span> <span class="number">16</span> <span class="number">18</span> <span class="number">1</span>C <span class="number">1</span>D <span class="number">1</span>E <span class="number">1F</span> <span class="number">20</span> <span class="number">21</span> <span class="number">22</span> </span><br><span class="line">   FF FF FF FF FF FF FF FF <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> </span><br><span class="line">   <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> B7 <span class="number">01</span>];</span><br><span class="line">  status = <span class="string">"okay"</span>;</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*省略不相关部分*/</span></span><br><span class="line">}</span><br><span class="line">                </span><br></pre></td></tr></table></figure></div><h2 id="D-驱动编写"><a href="#D-驱动编写" class="headerlink" title="D 驱动编写"></a>D 驱动编写</h2><p>驱动主要涉及 IIC框架，测试中断，INPUT子系统框架，测试上报数据，测试屏幕</p><p>下面先给出完整代码，包括单点触摸和多点触摸（其中因为gt911不支持硬件检测每个触摸点的按下和抬起，所以多点效果并不好，可以通过驱动文件中最上面的宏MORE_GT911选择使用多点或单点）</p><p>大致说一下代码流程:</p><ol><li>首先通过<strong>IIC的匹配表</strong>（module_i2c_driver(gt911_i2c_driver);），如果匹配成功，进入gt911_probe()函数</li><li>首先获取设备树种的中断和复位引脚，然后<strong>根据gt911的复位和初始化时序，来初始化屏幕</strong></li><li>这时候可以先初始化中断，然后便能通过中断中添加调试信息来判断是否初始化成功，但这里有一点要注意的，gt911要求触发中断之后，需要主要<strong>清除中断标志位</strong>，才能正常响应下一次中断(  data = 0x00; ret = gt911_write_regs(dev, GT_GSTID_REG, &amp;data, 1))，所以要<strong>提前写好IIC的read和write函数</strong>,成功之后当你按屏幕后，将会有调试信息输出</li><li>确认中断无误后，可以<strong>在probe中添加INPUT子系统的初始化</strong>,以及对屏幕的软复位，同时将中断的初始化放到probe函数最后</li><li>最重要的一步就是<strong>在中断函数中添加对触摸信息的上报同步</strong>，这里要注意上报的顺序，其中中断使用的是<strong>中断线程化</strong></li></ol><p>下面再具体说说驱动中的几个重点:</p><ul><li><strong>上报流程</strong></li></ul><p>这里先简单介绍一下多点触摸(MT)协议，通常有两种: <strong>TypeA</strong>(适用于触摸点不能被区分和追踪的硬件) 和 <strong>TypeB</strong>(适用于有硬件追踪并能区分触摸点的触摸设备) 两种</p><p>下面具体说说TypeB 的上报时序(这里以两个触摸点来表示)</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">ABS_MT_SLOT <span class="number">0</span>              <span class="comment">//触摸点的SLOT 即触摸点ID 需要由触摸IC提供</span></span><br><span class="line">ABS_MT_TRACKING_ID <span class="number">45</span>      <span class="comment">//完成对触摸点的添加、替换或删除，通过input_mt_report_slot_state实现</span></span><br><span class="line">ABS_MT_POSITION_X x[<span class="number">0</span>]     <span class="comment">//触摸点0的X坐标，通过input_report_abs函数来完成</span></span><br><span class="line">ABS_MT_POSITION_Y y[<span class="number">0</span>]     <span class="comment">//触摸点0的Y坐标</span></span><br><span class="line">ABS_MT_SLOT <span class="number">1</span></span><br><span class="line">ABS_MT_TRACKING_ID <span class="number">46</span></span><br><span class="line">ABS_MT_POSITION_X x[<span class="number">1</span>]</span><br><span class="line">ABS_MT_POSITION_Y y[<span class="number">1</span>]</span><br><span class="line">SYN_REPORT</span><br><span class="line"></span><br><span class="line"><span class="comment">/*根据 Type B 的要求，每个 SLOT 必须关联一个 ABS_MT_TRACKING_ID，通过</span></span><br><span class="line"><span class="comment">修改 SLOT 关联的 ABS_MT_TRACKING_ID 来完成对触摸点的添加、替换或删除。具体用到</span></span><br><span class="line"><span class="comment">的函数就是 input_mt_report_slot_state，如果是添加一个新的触摸点，那么此函数的第三个参数</span></span><br><span class="line"><span class="comment">active 要设置为 true，linux 内核会自动分配一个 ABS_MT_TRACKING_ID 值，不需要用户去指</span></span><br><span class="line"><span class="comment">定具体的 ABS_MT_TRACKING_ID 值 */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>在初始化INPUT子系统的时候，MT也有一个初始化slots的函数</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"> ret = input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, <span class="number">0</span>); <span class="comment">//多点触控需初始化(MY)槽</span></span><br><span class="line"> <span class="keyword">if</span> (ret) {</span><br><span class="line">  <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ret = input_register_device(gt911.input); <span class="comment">//注册input设备</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//下面是上报流程的部分代码</span></span><br><span class="line"><span class="keyword">if</span>(touch_num) {         <span class="comment">/* 单点触摸按下 */</span></span><br><span class="line">     gt911_read_regs(dev, GT_TP1_REG, touch_data, <span class="number">5</span>);</span><br><span class="line">    id = touch_data[<span class="number">0</span>] &amp; <span class="number">0x0F</span>;</span><br><span class="line">    <span class="keyword">if</span>(id == <span class="number">0</span>) {</span><br><span class="line">        input_x  = touch_data[<span class="number">1</span>] | (touch_data[<span class="number">2</span>] &lt;&lt; <span class="number">8</span>);</span><br><span class="line">        input_y  = touch_data[<span class="number">3</span>] | (touch_data[<span class="number">4</span>] &lt;&lt; <span class="number">8</span>);</span><br><span class="line">  input_mt_slot(dev-&gt;input, id);                                 <span class="comment">//上报id</span></span><br><span class="line">  input_mt_report_slot_state(dev-&gt;input, MT_TOOL_FINGER, <span class="literal">true</span>);  <span class="comment">//按下</span></span><br><span class="line">  input_report_abs(dev-&gt;input, ABS_MT_POSITION_X, input_x);      <span class="comment">//x</span></span><br><span class="line">  input_report_abs(dev-&gt;input, ABS_MT_POSITION_Y, input_y);      <span class="comment">//y</span></span><br><span class="line">    }</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span>(touch_num == <span class="number">0</span>){                <span class="comment">/* 单点触摸释放 */</span></span><br><span class="line">    input_mt_slot(dev-&gt;input, id);</span><br><span class="line">    input_mt_report_slot_state(dev-&gt;input, MT_TOOL_FINGER, <span class="literal">false</span>); <span class="comment">//松开</span></span><br><span class="line">}</span><br><span class="line">input_mt_report_pointer_emulation(dev-&gt;input, <span class="literal">true</span>); <span class="comment">//若追踪的触摸点数量多于当前上报的数量，那么置设置为false</span></span><br><span class="line">input_sync(dev-&gt;input);      <span class="comment">//通知内核一次完整的事件上报已经结束</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>在触摸屏中我们使用到了三个事件如下</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">__set_bit(EV_KEY, gt911.input-&gt;evbit);  <span class="comment">//按键事件</span></span><br><span class="line">__set_bit(EV_ABS, gt911.input-&gt;evbit);  <span class="comment">//绝对坐标事件</span></span><br><span class="line">__set_bit(BTN_TOUCH, gt911.input-&gt;keybit); <span class="comment">//触摸按键</span></span><br></pre></td></tr></table></figure></div><p>通过中断，上报IIC读取到的数据到Linux内核完成触摸屏的驱动</p><ul><li><strong>中断线程化</strong></li></ul><p>这里主要介绍 <code>devm_request_threaded_irq</code> 函数，作用是<strong>申请中断</strong>，<strong>与request_irq类似</strong>，</p><p>1.其中的<strong>threaded是线程的意思</strong>，因为硬件中断具有很高的优先级，但是如果中断频繁进入就会打断其它任务，使得其他任务可能得不到及时的处理。这里有人会问那为什么不使用中断的下半部，因为<strong>中断下半部的优先级仍旧很高</strong>（基本高于普通线程）</p><table><thead><tr><th>特性维度</th><th>软中断</th><th>Tasklet</th><th>工作队列</th><th>中断线程化</th></tr></thead><tbody><tr><td><strong>本质/关系</strong></td><td>传统下半部核心机制，基于中断上下文</td><td>基于软中断实现，更易用</td><td>传统下半部机制，运行于内核线程上下文</td><td>中断下半部的一种实现形式，但采用线程模型</td></tr><tr><td><strong>执行上下文</strong></td><td><strong>中断上下文</strong>（软中断上下文）</td><td><strong>中断上下文</strong>（软中断上下文）</td><td><strong>进程上下文</strong>（普通内核线程）</td><td><strong>进程上下文</strong>（专用实时内核线程）</td></tr><tr><td><strong>是否可阻塞/睡眠</strong></td><td><strong>否</strong></td><td><strong>否</strong></td><td><strong>是</strong></td><td><strong>是</strong></td></tr><tr><td><strong>优先级</strong></td><td>最高（在中断上下文执行）</td><td>较高</td><td>较低（普通线程调度）</td><td>可调度的实时优先级（通常高于普通工作队列）</td></tr><tr><td><strong>并发特性</strong></td><td>同一种类可在多CPU上<strong>并行</strong>，需重入设计</td><td>同一tasklet在多个CPU上<strong>串行</strong>执行</td><td>可配置，但同一工作队列中的任务可能串行执行</td><td>每个中断有<strong>独立线程</strong>，可绑定到特定CPU</td></tr><tr><td><strong>编程复杂度</strong></td><td>高（需考虑重入和并发安全）</td><td>中（自动序列化简化编程）</td><td>低（类似普通内核编程）</td><td>低（类似普通线程编程）</td></tr><tr><td><strong>适用场景</strong></td><td>高频、实时性要求极高的场景（如网络包处理）</td><td>简单的设备中断处理，需要避免并发问题</td><td>耗时操作，需要睡眠或阻塞的场景（如文件I/O）</td><td>复杂的驱动处理，需要实时性保证和可睡眠能力</td></tr></tbody></table><p>当然也<strong>不是所有的中断都可以被线程化</strong>，比如那些重要的中断，只是因为屏幕的触摸<strong>很频繁，且不是那么重要</strong>，所以可以线程化</p><p>GT911 通过 I2C 总线与主控芯片通信，在中断处理函数中需要读取触摸数据。<strong>I2C 传输本身并不是一个原子操作，在访问过程中，如果控制器繁忙，可能会导致等待</strong>。在<strong>中断上下文（软中断运行环境）中，这种等待（即使是极短的）是绝对禁止的</strong>，因为它会阻塞整个系统的中断响应。而<strong>中断线程化在进程上下文中运行，​允许睡眠和阻塞</strong>，所以这里中断线程化更优</p><p>2.devm的意思是<strong>使用“devm_”前缀的函数申请到的资源可以由系统自动释放，不需要我们手动处理</strong></p><ul><li><strong>维护数据</strong></li></ul><p>这里可以通过三种方式维护:</p><ol><li><p><strong>数据存储位置</strong> 触摸屏中读取的数据通常存储在驱动的私有数据结构体中，在这里可以分配一个缓冲区来保存坐标状态等信息</p></li><li><p><strong>防止多核访问冲突</strong> 这里可以使用linux内核的锁机制 <strong>自旋锁</strong>(适用于短时间的临界区保护） <strong>互斥锁</strong>(适用于需要睡眠的场景) 或者 在中断上下文<strong>禁用中断</strong></p></li><li><p><strong>数据更新与同步</strong> 在中断处理函数中读取触摸数据后，需要<strong>及时更新到缓冲区</strong>，并通过同步机制通知其他线程或内核子系统(input_sync(dev-&gt;input))</p></li></ol><p>因为之前我们选择的是<strong>进程上下文(中断线程化或者工作队列，允许睡眠)，所以可以选择互斥锁</strong><br>当然，如果选择<strong>使用中断上下文(比如中断下半部或者tasklet，不运行睡眠)，可以选择自旋锁</strong></p><ul><li><strong>驱动代码</strong></li></ul><p>下面直接贴源码了(基于前面写的设备树和imx6ullm板子)</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * gt911驱动 </span></span><br><span class="line"><span class="comment"> * obj:gt911_mouse.ko</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/module.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/i2c.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/regmap.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/gpio/consumer.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/of_irq.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/interrupt.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/input.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/input/mt.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/debugfs.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/delay.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/slab.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/gpio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/of_gpio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/input/mt.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/input/touchscreen.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/i2c.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_CTRL_REG          0X8040  <span class="comment">/* gt911控制寄存器         */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_MODSW_REG          0X804D  <span class="comment">/* gt911模式切换寄存器        */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_CFGS_REG          0X8047  <span class="comment">/* gt911配置起始地址寄存器    */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_CHECK_REG          0X80FF  <span class="comment">/* gt911校验和寄存器       */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_PID_REG           0X8140  <span class="comment">/* gt911产品ID寄存器       */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_GSTID_REG          0X814E  <span class="comment">/* gt911当前检测到的触摸情况 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_TP1_REG           0X814F  <span class="comment">/* 第一个触摸点数据地址 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_TP2_REG           0X8157 <span class="comment">/* 第二个触摸点数据地址 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_TP3_REG           0X815F  <span class="comment">/* 第三个触摸点数据地址 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_TP4_REG           0X8167  <span class="comment">/* 第四个触摸点数据地址  */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT_TP5_REG           0X816F <span class="comment">/* 第五个触摸点数据地址   */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX_SUPPORT_POINTS      3       <span class="comment">/* 最多5点电容触摸 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MORE_GT911    0  <span class="comment">/* 是否支持多点 */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GT911_TP_DATA_SIZE 8    <span class="comment">// 每个触摸点数据长度</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BUFFER_SIZE (GT911_TP_DATA_SIZE * MAX_SUPPORT_POINTS) <span class="comment">// 读取寄存器时的缓存大小</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">gt911_dev</span> {</span></span><br><span class="line"> <span class="type">int</span> irq_pin,reset_pin;     <span class="comment">/* 中断和复位IO  */</span></span><br><span class="line"> <span class="type">int</span> irqnum;        <span class="comment">/* 中断号      */</span></span><br><span class="line"> <span class="type">void</span> *private_data;      <span class="comment">/* 私有数据   */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">input_dev</span> *<span class="title">input</span>;</span>    <span class="comment">/* input结构体   */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_client</span> *<span class="title">client</span>;</span>    <span class="comment">/* I2C客户端   */</span></span><br><span class="line"></span><br><span class="line">};</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">gt911_dev</span> <span class="title">gt911</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> gt911_CT[]=</span><br><span class="line">{</span><br><span class="line"> <span class="number">0x48</span>,<span class="number">0xe0</span>,<span class="number">0x01</span>,<span class="number">0x10</span>,<span class="number">0x01</span>,<span class="number">0x05</span>,<span class="number">0x0d</span>,<span class="number">0x00</span>,<span class="number">0x01</span>,<span class="number">0x08</span>,</span><br><span class="line"> <span class="number">0x28</span>,<span class="number">0x05</span>,<span class="number">0x50</span>,<span class="number">0x32</span>,<span class="number">0x03</span>,<span class="number">0x05</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x89</span>,<span class="number">0x28</span>,<span class="number">0x0a</span>,</span><br><span class="line"> <span class="number">0x17</span>,<span class="number">0x15</span>,<span class="number">0x31</span>,<span class="number">0x0d</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x02</span>,<span class="number">0x9b</span>,<span class="number">0x03</span>,<span class="number">0x25</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x32</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x0f</span>,<span class="number">0x94</span>,<span class="number">0x94</span>,<span class="number">0xc5</span>,<span class="number">0x02</span>,<span class="number">0x07</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x04</span>,</span><br><span class="line"> <span class="number">0x8d</span>,<span class="number">0x13</span>,<span class="number">0x00</span>,<span class="number">0x5c</span>,<span class="number">0x1e</span>,<span class="number">0x00</span>,<span class="number">0x3c</span>,<span class="number">0x30</span>,<span class="number">0x00</span>,<span class="number">0x29</span>,</span><br><span class="line"> <span class="number">0x4c</span>,<span class="number">0x00</span>,<span class="number">0x1e</span>,<span class="number">0x78</span>,<span class="number">0x00</span>,<span class="number">0x1e</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x08</span>,<span class="number">0x0a</span>,<span class="number">0x0c</span>,<span class="number">0x0e</span>,<span class="number">0x10</span>,<span class="number">0x12</span>,<span class="number">0x14</span>,<span class="number">0x16</span>,</span><br><span class="line"> <span class="number">0x18</span>,<span class="number">0x1a</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x1f</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,</span><br><span class="line"> <span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,</span><br><span class="line"> <span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0x00</span>,<span class="number">0x02</span>,<span class="number">0x04</span>,<span class="number">0x05</span>,<span class="number">0x06</span>,<span class="number">0x08</span>,<span class="number">0x0a</span>,<span class="number">0x0c</span>,</span><br><span class="line"> <span class="number">0x0e</span>,<span class="number">0x1d</span>,<span class="number">0x1e</span>,<span class="number">0x1f</span>,<span class="number">0x20</span>,<span class="number">0x22</span>,<span class="number">0x24</span>,<span class="number">0x28</span>,<span class="number">0x29</span>,<span class="number">0xff</span>,</span><br><span class="line"> <span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,</span><br><span class="line"> <span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,</span><br><span class="line"> <span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,<span class="number">0xff</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @description     : 复位gt911</span></span><br><span class="line"><span class="comment"> * @param - client  : 要操作的i2c</span></span><br><span class="line"><span class="comment"> * @param - multidev: 自定义的multitouch设备</span></span><br><span class="line"><span class="comment"> * @return          : 0，成功;其他负值,失败</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">gt911_ts_reset</span><span class="params">(<span class="keyword">struct</span> i2c_client *client, <span class="keyword">struct</span> gt911_dev *dev)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 申请复位IO*/</span></span><br><span class="line"> <span class="keyword">if</span> (gpio_is_valid(dev-&gt;reset_pin)) {    </span><br><span class="line">  <span class="comment">/* 申请复位IO，并且默认输出低电平 */</span></span><br><span class="line">  ret = devm_gpio_request_one(&amp;client-&gt;dev, </span><br><span class="line">     dev-&gt;reset_pin, GPIOF_OUT_INIT_LOW,</span><br><span class="line">     <span class="string">"gt911 reset"</span>);</span><br><span class="line">  <span class="keyword">if</span> (ret) {</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">  }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 申请中断IO*/</span></span><br><span class="line"> <span class="keyword">if</span> (gpio_is_valid(dev-&gt;irq_pin)) {    </span><br><span class="line">  <span class="comment">/* 申请复位IO，并且默认输出低电平 */</span></span><br><span class="line">  ret = devm_gpio_request_one(&amp;client-&gt;dev, </span><br><span class="line">     dev-&gt;irq_pin, GPIOF_OUT_INIT_LOW,</span><br><span class="line">     <span class="string">"gt911 irq"</span>);</span><br><span class="line">  <span class="keyword">if</span> (ret) {</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">  }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 4、初始化gt911，要严格按照gt911时序要求 */</span></span><br><span class="line">    gpio_set_value(dev-&gt;reset_pin, <span class="number">0</span>); <span class="comment">/* 复位gt911 */</span></span><br><span class="line">    msleep(<span class="number">10</span>);</span><br><span class="line">    gpio_set_value(dev-&gt;reset_pin, <span class="number">1</span>); <span class="comment">/* 停止复位gt911 */</span></span><br><span class="line">    msleep(<span class="number">10</span>);</span><br><span class="line">    gpio_set_value(dev-&gt;irq_pin, <span class="number">0</span>);    <span class="comment">/* 拉低INT引脚 */</span></span><br><span class="line">    msleep(<span class="number">50</span>);</span><br><span class="line">    gpio_direction_input(dev-&gt;irq_pin); <span class="comment">/* INT引脚设置为输入 */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @description : 从gt911读取多个寄存器数据</span></span><br><span class="line"><span class="comment"> * @param - dev:  gt911设备</span></span><br><span class="line"><span class="comment"> * @param - reg:  要读取的寄存器首地址</span></span><br><span class="line"><span class="comment"> * @param - buf:  读取到的数据</span></span><br><span class="line"><span class="comment"> * @param - len:  要读取的数据长度</span></span><br><span class="line"><span class="comment"> * @return   : 操作结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">gt911_read_regs</span><span class="params">(<span class="keyword">struct</span> gt911_dev *dev, u16 reg, u8 *buf, <span class="type">int</span> len)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> ret;</span><br><span class="line">    u8 regdata[<span class="number">2</span>];</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_msg</span> <span class="title">msg</span>[2];</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_client</span> *<span class="title">client</span> =</span> (<span class="keyword">struct</span> i2c_client *)dev-&gt;client;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/* gt911寄存器长度为2个字节 */</span></span><br><span class="line">    regdata[<span class="number">0</span>] = reg &gt;&gt; <span class="number">8</span>;</span><br><span class="line">    regdata[<span class="number">1</span>] = reg &amp; <span class="number">0xFF</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* msg[0]为发送要读取的首地址 */</span></span><br><span class="line"> msg[<span class="number">0</span>].addr = client-&gt;addr;   <span class="comment">/* ft5x06地址 */</span></span><br><span class="line"> msg[<span class="number">0</span>].flags = !I2C_M_RD;   <span class="comment">/* 标记为发送数据 */</span></span><br><span class="line"> msg[<span class="number">0</span>].buf = &amp;regdata[<span class="number">0</span>];   <span class="comment">/* 读取的首地址 */</span></span><br><span class="line"> msg[<span class="number">0</span>].len = <span class="number">2</span>;      <span class="comment">/* reg长度*/</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* msg[1]读取数据 */</span></span><br><span class="line"> msg[<span class="number">1</span>].addr = client-&gt;addr;   <span class="comment">/* ft5x06地址 */</span></span><br><span class="line"> msg[<span class="number">1</span>].flags = I2C_M_RD;   <span class="comment">/* 标记为读取数据*/</span></span><br><span class="line"> msg[<span class="number">1</span>].buf = buf;     <span class="comment">/* 读取数据缓冲区 */</span></span><br><span class="line"> msg[<span class="number">1</span>].len = len;     <span class="comment">/* 要读取的数据长度*/</span></span><br><span class="line"></span><br><span class="line"> ret = i2c_transfer(client-&gt;adapter, msg, <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">if</span>(ret == <span class="number">2</span>) {</span><br><span class="line">  ret = <span class="number">0</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line">  ret = -EREMOTEIO;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @description : 向gt911多个寄存器写入数据</span></span><br><span class="line"><span class="comment"> * @param - dev:  gt911设备</span></span><br><span class="line"><span class="comment"> * @param - reg:  要写入的寄存器首地址</span></span><br><span class="line"><span class="comment"> * @param - val:  要写入的数据缓冲区</span></span><br><span class="line"><span class="comment"> * @param - len:  要写入的数据长度</span></span><br><span class="line"><span class="comment"> * @return    :   操作结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> s32 <span class="title function_">gt911_write_regs</span><span class="params">(<span class="keyword">struct</span> gt911_dev *dev, u16 reg, u8 *buf, u8 len)</span></span><br><span class="line">{</span><br><span class="line"> u8 b[<span class="number">256</span>];</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_msg</span> <span class="title">msg</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_client</span> *<span class="title">client</span> =</span> (<span class="keyword">struct</span> i2c_client *)dev-&gt;client;</span><br><span class="line"> </span><br><span class="line"> b[<span class="number">0</span>] = reg &gt;&gt; <span class="number">8</span>;   <span class="comment">/* 寄存器首地址低8位 */</span></span><br><span class="line">    b[<span class="number">1</span>] = reg &amp; <span class="number">0XFF</span>;   <span class="comment">/* 寄存器首地址高8位 */</span></span><br><span class="line"> <span class="built_in">memcpy</span>(&amp;b[<span class="number">2</span>],buf,len);  <span class="comment">/* 将要写入的数据拷贝到数组b里面 */</span></span><br><span class="line"></span><br><span class="line"> msg.addr = client-&gt;addr; <span class="comment">/* gt911地址 */</span></span><br><span class="line"> msg.flags = <span class="number">0</span>;    <span class="comment">/* 标记为写数据 */</span></span><br><span class="line"></span><br><span class="line"> msg.buf = b;    <span class="comment">/* 要写入的数据缓冲区 */</span></span><br><span class="line"> msg.len = len + <span class="number">2</span>;   <span class="comment">/* 要写入的数据长度 */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> i2c_transfer(client-&gt;adapter, &amp;msg, <span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> MORE_GT911 == 0</span></span><br><span class="line"><span class="type">static</span> <span class="type">irqreturn_t</span> <span class="title function_">gt911_irq_handler</span><span class="params">(<span class="type">int</span> irq, <span class="type">void</span> *dev_id)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> touch_num = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> input_x, input_y;</span><br><span class="line">    <span class="type">int</span> id = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">    u8 data;</span><br><span class="line">    u8 touch_data[<span class="number">5</span>];</span><br><span class="line">    <span class="class"><span class="keyword">struct</span>  <span class="title">gt911_dev</span> *<span class="title">dev</span> =</span> dev_id;</span><br><span class="line"></span><br><span class="line">    ret =  gt911_read_regs(dev, GT_GSTID_REG, &amp;data, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (data == <span class="number">0x00</span>)  {     <span class="comment">/* 没有触摸数据，直接返回 */</span></span><br><span class="line">        <span class="keyword">goto</span> fail;</span><br><span class="line">    } <span class="keyword">else</span> {                 <span class="comment">/* 统计触摸点数据 */</span></span><br><span class="line">        touch_num = data &amp; <span class="number">0x0f</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 由于 gt911没有硬件检测每个触摸点按下和抬起，因此每个触摸点的抬起和按</span></span><br><span class="line"><span class="comment">     * 下不好处理，尝试过一些方法，但是效果都不好，因此这里暂时使用单点触摸 </span></span><br><span class="line"><span class="comment">  * 下面有一个支持多点触摸的，但是连续性不好</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">if</span>(touch_num) {         <span class="comment">/* 单点触摸按下 */</span></span><br><span class="line">         gt911_read_regs(dev, GT_TP1_REG, touch_data, <span class="number">5</span>);</span><br><span class="line">        id = touch_data[<span class="number">0</span>] &amp; <span class="number">0x0F</span>;</span><br><span class="line">        <span class="keyword">if</span>(id == <span class="number">0</span>) {</span><br><span class="line">            input_x  = touch_data[<span class="number">1</span>] | (touch_data[<span class="number">2</span>] &lt;&lt; <span class="number">8</span>);</span><br><span class="line">            input_y  = touch_data[<span class="number">3</span>] | (touch_data[<span class="number">4</span>] &lt;&lt; <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">      input_mt_slot(dev-&gt;input, id);                                 <span class="comment">//上报id</span></span><br><span class="line">      input_mt_report_slot_state(dev-&gt;input, MT_TOOL_FINGER, <span class="literal">true</span>);  <span class="comment">//按下</span></span><br><span class="line">      input_report_abs(dev-&gt;input, ABS_MT_POSITION_X, input_x);      <span class="comment">//x</span></span><br><span class="line">      input_report_abs(dev-&gt;input, ABS_MT_POSITION_Y, input_y);      <span class="comment">//y</span></span><br><span class="line">        }</span><br><span class="line">    } <span class="keyword">else</span> <span class="keyword">if</span>(touch_num == <span class="number">0</span>){                <span class="comment">/* 单点触摸释放 */</span></span><br><span class="line">        input_mt_slot(dev-&gt;input, id);</span><br><span class="line">        input_mt_report_slot_state(dev-&gt;input, MT_TOOL_FINGER, <span class="literal">false</span>); <span class="comment">//松开</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    input_mt_report_pointer_emulation(dev-&gt;input, <span class="literal">true</span>); <span class="comment">//若追踪的触摸点数量多于当前上报的数量，那么设置设置为fase</span></span><br><span class="line">    input_sync(dev-&gt;input);      <span class="comment">//同步使用</span></span><br><span class="line"></span><br><span class="line">    data = <span class="number">0x00</span>;                <span class="comment">/* 向0X814E寄存器写0 */</span></span><br><span class="line">     gt911_write_regs(dev, GT_GSTID_REG, &amp;data, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">fail:</span><br><span class="line"> <span class="keyword">return</span> IRQ_HANDLED;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//支持多点触摸的触摸屏中断函数</span></span><br><span class="line"><span class="type">static</span> <span class="type">irqreturn_t</span> <span class="title function_">gt911_irq_handler</span><span class="params">(<span class="type">int</span> irq, <span class="type">void</span> *dev_id)</span></span><br><span class="line">{</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">gt911_dev</span> *<span class="title">dev</span> =</span> dev_id;</span><br><span class="line">    <span class="type">int</span> input_x, input_y;</span><br><span class="line">    <span class="type">int</span> i,id = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line"> u8 data;</span><br><span class="line">    <span class="type">int</span> touch_num = <span class="number">0</span>;  <span class="comment">// 触摸点数量</span></span><br><span class="line"> u8 buf[BUFFER_SIZE];  <span class="comment">// 触摸数据缓冲区</span></span><br><span class="line">    u16 reported_ids = <span class="number">0</span>;      <span class="comment">// 用于记录本轮已上报的ID，用于判断哪些点抬起</span></span><br><span class="line">    <span class="type">static</span> u16 last_reported_ids = <span class="number">0</span>; <span class="comment">// 上一轮已上报的ID</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//1,判断是否有触摸屏数据</span></span><br><span class="line">    ret = gt911_read_regs(dev, GT_GSTID_REG, &amp;data, <span class="number">1</span>); </span><br><span class="line">    <span class="keyword">if</span> (data == <span class="number">0x00</span>)  <span class="comment">//没有数据直接返回</span></span><br><span class="line"> {  </span><br><span class="line">        <span class="keyword">goto</span> clear_irq; </span><br><span class="line">    } </span><br><span class="line"> <span class="keyword">else</span> </span><br><span class="line"> {</span><br><span class="line">  touch_num = data &amp; <span class="number">0x0f</span>; <span class="comment">//统计触摸点个数</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//2,读取所有触摸点的数据</span></span><br><span class="line">    <span class="keyword">if</span>(touch_num&gt;<span class="number">0</span>) </span><br><span class="line"> { </span><br><span class="line">        ret = gt911_read_regs(dev, GT_TP1_REG, buf, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">  <span class="keyword">if</span>(ret&lt;<span class="number">0</span>)</span><br><span class="line">  {</span><br><span class="line">   <span class="keyword">goto</span> clear_irq;</span><br><span class="line">  } </span><br><span class="line">    }</span><br><span class="line"> <span class="keyword">if</span>(touch_num&gt;MAX_SUPPORT_POINTS)touch_num=MAX_SUPPORT_POINTS;</span><br><span class="line"> <span class="comment">//3,上报有效的触摸点 按下或移动</span></span><br><span class="line"> <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;touch_num;i++)</span><br><span class="line"> {</span><br><span class="line">  <span class="type">int</span> pos = i*GT911_TP_DATA_SIZE; <span class="comment">//当前位置</span></span><br><span class="line">  id = buf[pos] &amp; <span class="number">0x0f</span>;   <span class="comment">//计算坐标</span></span><br><span class="line">  input_x = (buf[pos + <span class="number">1</span>] | (buf[pos + <span class="number">2</span>] &lt;&lt; <span class="number">8</span>)) &amp; <span class="number">0x0fff</span>;</span><br><span class="line">        input_y = (buf[pos + <span class="number">3</span>] | (buf[pos + <span class="number">4</span>] &lt;&lt; <span class="number">8</span>)) &amp; <span class="number">0x0fff</span>;</span><br><span class="line"></span><br><span class="line">  reported_ids |= (<span class="number">1</span> &lt;&lt; id); <span class="comment">//标记该id在本轮被上报了</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">//上报当前点数据</span></span><br><span class="line">  input_mt_slot(dev-&gt;input, id);</span><br><span class="line">        input_mt_report_slot_state(dev-&gt;input, MT_TOOL_FINGER, <span class="literal">true</span>);</span><br><span class="line">        input_report_abs(dev-&gt;input, ABS_MT_POSITION_X, input_x);</span><br><span class="line">        input_report_abs(dev-&gt;input, ABS_MT_POSITION_Y, input_y);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 4. 处理已抬起的触摸点 */</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; MAX_SUPPORT_POINTS; i++) </span><br><span class="line"> {</span><br><span class="line">        <span class="comment">// 如果这个点上一轮存在，但本轮不存在，说明已抬起</span></span><br><span class="line">        <span class="keyword">if</span> ((last_reported_ids &amp; (<span class="number">1</span> &lt;&lt; i)) &amp;&amp; !(reported_ids &amp; (<span class="number">1</span> &lt;&lt; i))) </span><br><span class="line">  {</span><br><span class="line">            input_mt_slot(dev-&gt;input, i);</span><br><span class="line">            input_mt_report_slot_state(dev-&gt;input, MT_TOOL_FINGER, <span class="literal">false</span>);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 5. 更新状态并同步 */</span></span><br><span class="line">    last_reported_ids = reported_ids;</span><br><span class="line">    input_mt_report_pointer_emulation(dev-&gt;input, <span class="literal">true</span>);</span><br><span class="line">    input_sync(dev-&gt;input);</span><br><span class="line"></span><br><span class="line">clear_irq:</span><br><span class="line">    <span class="comment">/* 清理中断状态（保持硬件能继续产生中断） */</span></span><br><span class="line">    data = <span class="number">0x00</span>;</span><br><span class="line">    ret = gt911_write_regs(dev, GT_GSTID_REG, &amp;data, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (ret &lt; <span class="number">0</span>) </span><br><span class="line"> {</span><br><span class="line">        printk(KERN_ERR <span class="string">"GT911: Failed to clear interrupt status\n"</span>);</span><br><span class="line">    }</span><br><span class="line"> <span class="keyword">return</span> IRQ_HANDLED;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @description     : gt911中断初始化</span></span><br><span class="line"><span class="comment"> * @param - client  : 要操作的i2c</span></span><br><span class="line"><span class="comment"> * @param - multidev: 自定义的multitouch设备</span></span><br><span class="line"><span class="comment"> * @return          : 0，成功;其他负值,失败</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">gt911_ts_irq</span><span class="params">(<span class="keyword">struct</span> i2c_client *client, <span class="keyword">struct</span> gt911_dev *dev)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 2，申请中断,client-&gt;irq就是IO中断， 上升沿*/</span></span><br><span class="line"> ret = devm_request_threaded_irq(&amp;client-&gt;dev, client-&gt;irq, <span class="literal">NULL</span>,</span><br><span class="line">     gt911_irq_handler,  IRQ_TYPE_EDGE_RISING | IRQF_ONESHOT,</span><br><span class="line">     client-&gt;name, &amp;gt911);</span><br><span class="line"> <span class="keyword">if</span> (ret) {</span><br><span class="line">  dev_err(&amp;client-&gt;dev, <span class="string">"Unable to request touchscreen IRQ.\n"</span>);</span><br><span class="line">  <span class="keyword">return</span> ret;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @description : 发送gt911配置参数</span></span><br><span class="line"><span class="comment"> * @param - client: i2c_client</span></span><br><span class="line"><span class="comment"> * @param - mode: 0,参数不保存到flash</span></span><br><span class="line"><span class="comment"> *                1,参数保存到flash</span></span><br><span class="line"><span class="comment"> * @return   : 无</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">gt911_send_cfg</span><span class="params">(<span class="keyword">struct</span> gt911_dev *dev, <span class="type">unsigned</span> <span class="type">char</span> mode)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">char</span> buf[<span class="number">2</span>];</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> buf[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> buf[<span class="number">1</span>] = mode; <span class="comment">/* 是否写入到gt911 FLASH?  即是否掉电保存 */</span></span><br><span class="line"> <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; (<span class="keyword">sizeof</span>(gt911_CT)); i++) <span class="comment">/* 计算校验和 */</span></span><br><span class="line">        buf[<span class="number">0</span>] += gt911_CT[i];            </span><br><span class="line">    buf[<span class="number">0</span>] = (~buf[<span class="number">0</span>]) + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 发送寄存器配置 */</span></span><br><span class="line">    gt911_write_regs(dev, GT_CFGS_REG, (u8 *)gt911_CT, <span class="keyword">sizeof</span>(gt911_CT));</span><br><span class="line">    gt911_write_regs(dev, GT_CHECK_REG, buf, <span class="number">2</span>);<span class="comment">/* 写入校验和,配置更新标记 */</span></span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">gt911_probe</span><span class="params">(<span class="keyword">struct</span> i2c_client *client, <span class="type">const</span> <span class="keyword">struct</span> i2c_device_id *id)</span></span><br><span class="line">{</span><br><span class="line">    u8 data, ret;</span><br><span class="line"></span><br><span class="line">    gt911.client = client;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* 1，获取设备树中的中断和复位引脚 */</span></span><br><span class="line"> gt911.irq_pin = of_get_named_gpio(client-&gt;dev.of_node, <span class="string">"interrupt-gpios"</span>, <span class="number">0</span>);</span><br><span class="line"> gt911.reset_pin = of_get_named_gpio(client-&gt;dev.of_node, <span class="string">"reset-gpios"</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> printk(<span class="string">"irq:%d reset:%d\r\n"</span>,gt911.irq_pin,gt911.reset_pin); <span class="comment">//9 137</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 2，复位gt911 */</span></span><br><span class="line"> ret = gt911_ts_reset(client, &amp;gt911);</span><br><span class="line"> <span class="keyword">if</span>(ret &lt; <span class="number">0</span>) {</span><br><span class="line">  <span class="keyword">goto</span> fail;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 3，初始化gt911 */</span></span><br><span class="line">    data = <span class="number">0x02</span>;</span><br><span class="line">    gt911_write_regs(&amp;gt911, GT_CTRL_REG, &amp;data, <span class="number">1</span>); <span class="comment">/* 软复位 */</span></span><br><span class="line">    mdelay(<span class="number">100</span>);</span><br><span class="line">    data = <span class="number">0x0</span>;</span><br><span class="line">    gt911_write_regs(&amp;gt911, GT_CTRL_REG, &amp;data, <span class="number">1</span>); <span class="comment">/* 停止软复位 */</span></span><br><span class="line">    mdelay(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 4,初始化gt911，烧写固件 </span></span><br><span class="line"><span class="comment">    gt911_read_regs(&amp;gt911, GT_CFGS_REG, &amp;data, 1);</span></span><br><span class="line"><span class="comment">    printk("gt911 ID =%#X\r\n", data);</span></span><br><span class="line"><span class="comment">    if(data &lt;  gt911_CT[0]) {</span></span><br><span class="line"><span class="comment">       gt911_send_cfg(&amp;gt911, 0);   芯片内置固件已能够使用，无需下载固件name</span></span><br><span class="line"><span class="comment">    } */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 5，input设备初始化和注册 */</span></span><br><span class="line">    gt911.input = devm_input_allocate_device(&amp;client-&gt;dev);</span><br><span class="line">    <span class="keyword">if</span> (!gt911.input) {</span><br><span class="line">     ret = -ENOMEM;</span><br><span class="line">     <span class="keyword">goto</span> fail;</span><br><span class="line">    }</span><br><span class="line">    gt911.input-&gt;name = client-&gt;name;</span><br><span class="line">    gt911.input-&gt;id.bustype = BUS_I2C;</span><br><span class="line">    gt911.input-&gt;dev.parent = &amp;client-&gt;dev;</span><br><span class="line">   </span><br><span class="line">    __set_bit(EV_KEY, gt911.input-&gt;evbit);  <span class="comment">//按键事件</span></span><br><span class="line">    __set_bit(EV_ABS, gt911.input-&gt;evbit);  <span class="comment">//绝对坐标事件</span></span><br><span class="line">    __set_bit(BTN_TOUCH, gt911.input-&gt;keybit); <span class="comment">//触摸按键</span></span><br><span class="line">   </span><br><span class="line">    <span class="comment">//单点触控</span></span><br><span class="line">    input_set_abs_params(gt911.input, ABS_X, <span class="number">0</span>, <span class="number">1024</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    input_set_abs_params(gt911.input, ABS_Y, <span class="number">0</span>, <span class="number">600</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">   </span><br><span class="line">    <span class="comment">//多点触控(最多5点)</span></span><br><span class="line">    input_set_abs_params(gt911.input, ABS_MT_POSITION_X,<span class="number">0</span>, <span class="number">1024</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    input_set_abs_params(gt911.input, ABS_MT_POSITION_Y,<span class="number">0</span>, <span class="number">600</span>, <span class="number">0</span>, <span class="number">0</span>); </span><br><span class="line">   </span><br><span class="line">    ret = input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, <span class="number">0</span>); <span class="comment">//多点触控需初始化(MY)槽</span></span><br><span class="line">    <span class="keyword">if</span> (ret) {</span><br><span class="line">     <span class="keyword">goto</span> fail;</span><br><span class="line">    }</span><br><span class="line">   </span><br><span class="line">    ret = input_register_device(gt911.input); <span class="comment">//注册input设备</span></span><br><span class="line">    <span class="keyword">if</span> (ret)</span><br><span class="line">     <span class="keyword">goto</span> fail;</span><br><span class="line">   </span><br><span class="line">       <span class="comment">// /* 6，最后初始化中断 */</span></span><br><span class="line">    ret = gt911_ts_irq(client, &amp;gt911);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>) {</span><br><span class="line">     <span class="keyword">goto</span> fail;</span><br><span class="line">    }</span><br><span class="line">       <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">   </span><br><span class="line">   fail:</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @description     : i2c驱动的remove函数，移除i2c驱动的时候此函数会执行</span></span><br><span class="line"><span class="comment"> * @param - client  : i2c设备</span></span><br><span class="line"><span class="comment"> * @return          : 0，成功;其他负值,失败</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">gt911_remove</span><span class="params">(<span class="keyword">struct</span> i2c_client *client)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">// input_unregister_device(gt911.input);</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> *  传统驱动匹配表</span></span><br><span class="line"><span class="comment"> */</span> </span><br><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_device_id</span> <span class="title">gt911_id_table</span>[] =</span> {</span><br><span class="line"> { <span class="string">"goodix,gt911"</span>, <span class="number">0</span>, },</span><br><span class="line">    { <span class="comment">/* sentinel */</span> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 设备树匹配表 </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">of_device_id</span> <span class="title">gt911_of_match_table</span>[] =</span> {</span><br><span class="line">    {.compatible = <span class="string">"goodix,gt911"</span> },</span><br><span class="line">    { <span class="comment">/* sentinel */</span> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* i2c驱动结构体 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">i2c_driver</span> <span class="title">gt911_i2c_driver</span> =</span> {</span><br><span class="line">    .driver = {</span><br><span class="line">        .name  = <span class="string">"gt911"</span>,</span><br><span class="line">        .owner = THIS_MODULE,</span><br><span class="line">        .of_match_table = gt911_of_match_table,</span><br><span class="line">    },</span><br><span class="line">    .id_table = gt911_id_table,</span><br><span class="line">    .probe  = gt911_probe,</span><br><span class="line">    .remove = gt911_remove,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">module_i2c_driver(gt911_i2c_driver);</span><br><span class="line">MODULE_LICENSE(<span class="string">"GPL"</span>);</span><br><span class="line">MODULE_AUTHOR(<span class="string">"Mouse_H"</span>);</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>最后可以将驱动添加到内核中，这样开发板启动就会自动启动gt911的驱动</p><h2 id="E-驱动测试"><a href="#E-驱动测试" class="headerlink" title="E 驱动测试"></a>E 驱动测试</h2><p>测试的方法有很多，首先可以使用 <strong>hexdump /dev/input/event2</strong> 来获取中断中上报的数据，注意观察自己是event几，否则可能没有信息，如果成功有数据上报，那么可以选择继续用专业的测试工具来测试<br>这里介绍一下<strong>tslib</strong>工具，大家可以在 <a class="link" href="https://github.com/kergoth/tslib">https://github.com/kergoth/tslib<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> 这里获取工具源码,解压安装编译下载后添加到开发板中，便可以实现手写测试屏幕的灵敏度或者偏移，具体教程可以自行在网上查找资料（比如正点原子I.MX6U 嵌入式 Linux 驱动开发指南 V1.81 第1566页）<br>下面有展示一下使用该工具的效果图:</p><img lazyload="" src="/images/loading.svg" data-src="/img/blog_word/Embedded_1/word_00001_066.webp" width="800"><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><a class="link" href="http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html">【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#x34;&#54;&#55;&#48;&#x32;&#x33;&#x36;&#x32;&#64;&#113;&#x71;&#46;&#x63;&#111;&#x6d;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">因为买的触摸屏是gt911的芯片，这篇文章简要介绍在imx6ull下如何驱动该屏幕，主要是为QT应用做基础，同时巩固驱动知识</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/Embedded/Linux/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="i.MX6ULL" scheme="https://blog.haozi-haozi.cn/tags/i-MX6ULL/"/>
    
    <category term="gt911" scheme="https://blog.haozi-haozi.cn/tags/gt911/"/>
    
  </entry>
  
  <entry>
    <title>i.MX6ULL-Linux 基础 &amp;&amp; QT移植 &amp;&amp; C 学习笔记</title>
    <link href="https://blog.haozi-haozi.cn/2025/08/20/embedded_imx6ull/"/>
    <id>https://blog.haozi-haozi.cn/2025/08/20/embedded_imx6ull/</id>
    <published>2025-08-20T11:30:55.000Z</published>
    <updated>2026-03-24T08:58:08.908Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-移植概念相关"><a href="#A-移植概念相关" class="headerlink" title="A 移植概念相关"></a>A 移植概念相关</h2><h3 id="1-Uboot启动流程"><a href="#1-Uboot启动流程" class="headerlink" title="1. Uboot启动流程"></a>1. Uboot启动流程</h3><pre class="mermaid">flowchart TD    A[芯片上电] --&gt; B[Boot ROM<br>固化在芯片内部的只读存储器]    B --&gt; C[根据启动拨码开关选择源<br>如: eMMC, SD Card, NAND Flash等]    C --&gt; D[Boot ROM 读取启动介质头信息<br>获取IVT, DCD等数据]    D --&gt; E{校验并加载<br>第一阶段引导程序}    E -- 例如SPL --&gt; F[初始化时钟, DDR等关键外设]    F -- 跳转 --&gt; G[加载U-Boot第二阶段至DDR]    G --&gt; H[U-Boot 第二阶段初始化<br>各种驱动和环境变量]    H --&gt; I{启动内核或进入命令行}    I -- 无操作或命令 --&gt; J[加载Linux内核]    I -- 用户打断 --&gt; K[进入U-Boot命令行]    J --&gt; L[启动操作系统]</pre><h3 id="2-Uboot移植"><a href="#2-Uboot移植" class="headerlink" title="2. Uboot移植"></a>2. Uboot移植</h3><h4 id="1-获取基础代码"><a href="#1-获取基础代码" class="headerlink" title="1. 获取基础代码"></a>1. 获取基础代码</h4><p>通过参考NXP官方的板子，在这个板子的<strong>Uboot代码基础上进行修改</strong>，我们参考的是mx6ull_14x14_evk_emmc_defconfig(硬件设计参考了哪块官方板卡，就使用哪块板卡的U-Boot作为基础)</p><hr><h4 id="2。编译官方的代码"><a href="#2。编译官方的代码" class="headerlink" title="2。编译官方的代码"></a>2。编译官方的代码</h4><p>编译官方代码，一是检查代码<strong>是否能成功编译</strong>，二是可以尝试代码是否可以在自己板子上运行，如果能运行，可以<strong>观察哪些驱动有问题，需要修改</strong></p><hr><h4 id="3-创建新板配置"><a href="#3-创建新板配置" class="headerlink" title="3.创建新板配置"></a>3.创建新板配置</h4><ul><li><p><strong>​添加默认配置文件</strong>：​​ 在 configs/目录下复制并修改官方板的配置文件（如 defconfig），更改名称和关键选项（指定新的板级头文件路径）</p></li><li><p><strong>添加板级头文件</strong> 在 include/configs/目录下复制并修改官方板的头文件（如 .h）。这是最主要的配置中心，用于设置DDR容量、环境变量、驱动使能（如网络、LCD）、设备地址等。</p></li><li><p><strong>​添加板级代码目录</strong>：​​ 在 board/vendor/（如 board/freescale/）下复制并修改官方板的板级目录。需要修改其中的 Makefile, Kconfig, 主源文件（如 .c文件）等，以适配新板的初始化代码（如GPIO、时钟）。</p></li></ul><hr><h4 id="4-修改驱动"><a href="#4-修改驱动" class="headerlink" title="4.修改驱动"></a>4.修改驱动</h4><p>这个板子主要修改两部分具体驱动，重点都是修改<strong>环境变量</strong>和<strong>参数</strong>与<strong>引脚配置</strong></p><ul><li><strong>LCD屏幕</strong></li></ul><p>需要根据自己的屏幕来修改，这里主要修改了</p><p> (xres, yres)：<strong>分辨率</strong>，​修改为LCD面板的实际物理分辨率，例如从默认的480x272修改为1024x600</p><p>pixfmt：<strong>像素格式</strong>，也就是一个像素点是多少位</p><p>pixclock：<strong>像素时钟</strong>，每个像素时钟周期的长度，单位为皮秒。</p><p>panel：<strong>panel环境变量</strong>主要用于<strong>指定当前使用的 LCD 屏幕的类型或名称</strong>，在板级头文件中</p><p>name：结构体(t display_info_t)中个有个成员变量mode，也是一个结构体(fb_videomode)，其中的成员（.name），也是指定LCD 屏幕的类型或名称,<strong>panel和name的值需要一样</strong></p><p>然后可以通过启动uboot，设置<strong>环境变量panel</strong></p><div class="code-container" data-rel="Shell"><figure class="iseeu highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">setenv panel TFT7016</span><br><span class="line">saveenv</span><br></pre></td></tr></table></figure></div><p><strong>将环境变量保存到EMMC中</strong>，然后重新启动,检查LCD状态，若还有问题，继续检查代码是否修改完善</p><ul><li><strong>网络驱动</strong></li></ul><p>正点原子的 I.MX6U-ALPHA 开发板提供了这两个网络接口，其中 ENET1 和 ENET2 都使用 <strong>SR8201F</strong> 作为 PHY 芯片。NXP 官方的I.MX6ULL EVK 开发板使用 <strong>KSZ8081</strong> 这颗 PHY 芯片</p><p>因为修改了PHY芯片，所以网络驱动修改的核心是<strong>适配PHY芯片型号​（驱动+地址）和正确控制复位引脚​（时序+延时）</strong>，正点原子教程通过删除原厂扩展芯片逻辑，添加自定义GPIO复位，并满足SR8201F的时序要求来完成移植</p><p>修改完成后通过配置相关变量</p><div class="code-container" data-rel="Shell"><figure class="iseeu highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">setenv ipaddr 192.168.1.55 //开发板 IP 地址</span><br><span class="line">setenv ethaddr b8:ae:1d:01:00:00 //开发板网卡 MAC 地址</span><br><span class="line">setenv gatewayip 192.168.1.1 //开发板默认网关</span><br><span class="line">setenv netmask 255.255.255.0 //开发板子网掩码</span><br><span class="line">setenv serverip 192.168.1.250 //服务器地址，也就是 Ubuntu 地址</span><br><span class="line">saveenv //保存环境变量</span><br></pre></td></tr></table></figure></div><p>然后通过ping命令检测是否修改成功</p><hr><ul><li><strong>配置环境变量bootcmd &amp; bootargs</strong></li></ul><h5 id="环境变量bootcmd"><a href="#环境变量bootcmd" class="headerlink" title="环境变量bootcmd"></a>环境变量bootcmd</h5><p>bootcmd 保存着 uboot 默认命令，uboot <strong>倒计时结束以后就会执行 bootcmd 中的命令</strong>。这些命令一般都是用来启动 Linux 内核的，比如<strong>读取 EMMC 或者 NAND Flash 中的 Linux 内核镜像文件和设备树文件到 DRAM 中，然后启动 Linux 内核</strong>。可以在 uboot 启动以后进入命令行设置 bootcmd 环境变量的值。如果 EMMC 或者 NAND 中没有保存 bootcmd 的值，那么 uboot 就会使用默认的值，<strong>板子第一次运行 uboot 的时候都会使用默认值来设置 bootcmd 环境变量</strong>，可以通过修改uboot文件的内容，设置bootcmd的默认值</p><p>下面主要说一下bootcmd具体内容:</p><div class="code-container" data-rel="Shell"><figure class="iseeu highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mmc dev 1 //切换到 EMMC</span><br><span class="line">fatload mmc 1:1 0x80800000 zImage //读取 zImage 到 0x80800000 处</span><br><span class="line">fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb //读取设备树到 0x83000000 处</span><br><span class="line">bootz 0x80800000 - 0x83000000 //启动 Linux</span><br></pre></td></tr></table></figure></div><p>总结就是<strong>从EMMC中读取zImage和dtb文件到内存中，然后启动linux</strong></p><p>当然在后面学习驱动的时候，通常是<strong>通过tftp来下载zImage和dtb文件到内存中，通过NFS挂载根文件系统</strong></p><div class="code-container" data-rel="Shell"><figure class="iseeu highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">tftp 80800000 zImage; </span><br><span class="line">tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; </span><br><span class="line">bootz 80800000 - 83000000'</span><br></pre></td></tr></table></figure></div><hr><h5 id="bootargs"><a href="#bootargs" class="headerlink" title="bootargs"></a>bootargs</h5><p>bootargs 保存着 uboot 传递给 Linux 内核的参数，分别是</p><ol><li><p><strong>console</strong>：console 用来设置 linux 终端(或者叫控制台)，也就是<strong>通过什么设备来和 Linux 进行交互</strong>，是串口还是 LCD 屏幕？如果是串口的话应该是串口几等等。一般设置串口作为 Linux 终端，这样我们就可以在电脑上通过 SecureCRT 来和 linux 交互了。这里设置 console 为 ttymxc0，因为 linux启动以后<strong>I.MX6ULL 的串口 1 在 linux 下的设备文件就是/dev/ttymxc0</strong>，在 Linux 下，一切皆文件。</p></li><li><p><strong>root</strong>：t 用来设置根文件系统的位置，例如root=/dev/mmcblk1p2 用于指明根文件系统存放在mmcblk1 设备的分区 2 中，而在学习驱动的时候，我们通常使用<strong>nfs挂载</strong></p></li></ol><div class="code-container" data-rel="Shell"><figure class="iseeu highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">//nfs挂载(其中的网络ip地址等要根据自己实际使用的更改)</span><br><span class="line">setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.250:/home/mouse/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.251:192.168.1.250:192.168.1.1:255.255.255.0::eth0:off' //设置 bootargs</span><br><span class="line">saveenv //保存环境变量</span><br><span class="line"></span><br><span class="line">//emmc启动</span><br><span class="line">setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw</span><br><span class="line">saveenv //保存环境变量</span><br></pre></td></tr></table></figure></div><p>root 后面有“rootwait rw”，rootwait 表示等待 mmc 设备初始化完成以后再挂载，否则的话mmc 设备还没初始化完成就挂载根文件系统会出错的。rw 表示根文件系统是可以读写的，不加rw 的话可能无法在根文件系统中进行写操作，只能进行读操作</p><p>附一张正点原子手册详解<br><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_064.webp" width="800"></p><h4 id="4-启动Uboot"><a href="#4-启动Uboot" class="headerlink" title="4.启动Uboot"></a>4.启动Uboot</h4><p>有两种启动方式，<strong>从EMMC启动 从网络启动(nfs)</strong>，前面已经说过了，启动需用用到的文件<strong>zImage(内核映像文件)<strong>和</strong>dtb文件(设备树文件)</strong>，将在内核源文件编译后生成</p><h3 id="3-内核启动流程（NULL）"><a href="#3-内核启动流程（NULL）" class="headerlink" title="3. 内核启动流程（NULL）"></a>3. 内核启动流程（NULL）</h3><p>很明显，我肯定没弄懂启动流程，所以附两个流程图大致了解一下</p><pre class="mermaid">graph TD    A[stext入口] --&gt; B[关闭MMU/D-cache]    B --&gt; C[校验处理器ID<br>__lookup_processor_type]    C --&gt; D[校验启动参数<br>__vet_atags]    D --&gt; E[创建页表<br>__create_page_tables]    E --&gt; F[使能MMU<br>__enable_mmu]    F --&gt; G[切换上下文<br>__mmap_switched]    G --&gt; H[start_kernel初始化]    H --&gt; I[rest_init创建进程]    I --&gt; J[创建init进程 PID=1]    I --&gt; K[创建kthreadd进程 PID=2]    I --&gt; L[进入idle进程 PID=0]    J --&gt; M[kernel_init_freeable]    M --&gt; N[挂载根文件系统<br>prepare_namespace]    M --&gt; O[执行用户空间init程序]    O --&gt; P[用户空间初始化]</pre><hr><pre class="mermaid">graph LR    内核态 --&gt;|mount rootfs| 根文件系统    根文件系统 --&gt;|exec /sbin/init| 用户态</pre><h3 id="4-内核移植"><a href="#4-内核移植" class="headerlink" title="4. 内核移植"></a>4. 内核移植</h3><h4 id="1-获取代码-编译-启动"><a href="#1-获取代码-编译-启动" class="headerlink" title="1. 获取代码-编译-启动"></a>1. 获取代码-编译-启动</h4><p>通常使用<strong>Linux 图形配置界面</strong>，uboot也有，但是因为修改部分比较少，所以未使用</p><p>然后复制添加<strong>设备树文件</strong>和<strong>开发板默认配置文件</strong>，编译下载仅板子测试，注意要<strong>先挂载一份好的根文件系统，否则会启动失败</strong></p><hr><h4 id="2-修改驱动"><a href="#2-修改驱动" class="headerlink" title="2. 修改驱动"></a>2. 修改驱动</h4><ul><li><strong>CPU 主频修改</strong> 当前 CPU 支持 198MHz、396MHz、528Mhz 和 792MHz 四种频率切换，其中调频策略为 ondemand，也就是定期检查负载，然后根据负载情况调节 CPU 频率。</li></ul><hr><ul><li><strong>修改 EMMC 驱动</strong></li></ul><ol><li><p><strong>使能 8 线 EMMC 驱动</strong>,Linux 内核驱动里面 EMMC 默认是 4 线模式的，4 线模式肯定没有 8 线模式的速度快,在设备树中修改</p></li><li><p><strong>关闭 EMMC 1.8V 供电选项</strong>,因为板子的EMMC供电应该为3.3V</p></li></ol><hr><ul><li><strong>底板网络驱动修改</strong></li></ul><p>和之前的uboot原因一样，因为<strong>使用的网络PHY芯片不同</strong>，前三点都在设备树文件中修改</p><ol><li>修改 SR8201F 的复位以及网络时钟引脚驱动</li><li>中修改 fec1 和 fec2 节点的 pinctrl-0 属性</li><li>修改 SR8201F 的 PHY 地址</li><li>修改 fec_main.c 文件 根据 SR8201F 收据手册上的要求，<strong>SR8201F 在复位结束以后需要等待至少 150ms 才能操作 SR8201F</strong>，因此这里添加了一个 200ms 的延时</li></ol><hr><h4 id="3-网络驱动测试"><a href="#3-网络驱动测试" class="headerlink" title="3. 网络驱动测试"></a>3. 网络驱动测试</h4><p>修改好设备树和 Linux 内核以后重新编译一下，得到新的 zImage 镜像文件和 dtb 设备树文件，使用网线连接后，ifconfig测试有几个网卡，然后打开网卡，用ping指令测试是否有网络</p><p>下面是正点原子总结的移植步骤:</p><blockquote><p>总结一下移植步骤：<br>①、在 Linux 内核中查找可以参考的板子，一般都是半导体厂商自己做的开发板。<br>②、编译出参考板子对应的 zImage 和.dtb 文件。<br>③、使用参考板子的 zImage 文件和.dtb 文件在我们所使用的板子上启动 Linux 内核，看能<br>否启动。<br>④、如果能启动的话就万事大吉，如果不能启动那就悲剧了，需要调试 Linux 内核。不过<br>一般都会参考半导体官方的开发板设计自己的硬件，所以大部分情况下都会启动起来。启动<br>Linux 内核用到的外设不多，一般就 DRAM(Uboot 都初始化好的)和串口。作为终端使用的串口<br>一般都会参考半导体厂商的 Demo 板。<br>⑤、修改相应的驱动，像 NAND Flash、EMMC、SD 卡等驱动官方的 Linux 内核都是已经<br>提供好了，基本不会出问题。重点是网络驱动，因为 Linux 驱动开发一般都要通过网络调试代<br>码，所以一定要确保网络驱动工作正常。如果是处理器内部 MAC+外部 PHY 这种网络方案的<br>话，一般网络驱动都很好处理，因为在 Linux 内核中是有外部 PHY 通用驱动的。只要设置好复<br>位引脚、PHY 地址信息基本上都可以驱动起来。<br>⑥、Linux 内核启动以后需要根文件系统，如果没有根文件系统的话肯定会崩溃，所以确定 Linux<br>内核移植成功以后就要开始根文件系统的构建。</p></blockquote><h3 id="5-根文件系统的构建"><a href="#5-根文件系统的构建" class="headerlink" title="5. 根文件系统的构建"></a>5. 根文件系统的构建</h3><p>根文件系统首先是内核启动时所 mount(挂载)的第一个文件系统，内核代码像文件保存在根文件系统中，而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。</p><p>教程中是<strong>使用BusyBox 构建根文件系统</strong>，主要修改两部分 <strong>添加编译器</strong> <strong>添加中文支持</strong>，选择<strong>动态编译</strong>后编译 busybox，会得到一个基础的根文件系统(BusyBox的工作也就到此为止)</p><p>然后是向根文件系统中添加向根文件系统添加 lib 库(动态库，如果是静态编译则不用，但是静态库很占内存，并且可能导致dns不能使用)和一些基础文件目录(如 dev、proc、mnt、sys、tmp 和 root 等)，这里的<strong>lib库通过交叉编译器工具中获取</strong>，并且<strong>没有裁剪</strong>，是为了避免初学时因库文件缺失而带来的各种问题，裁剪是后续优化的工作，目前全部添加，内存就已经达到124MB。</p><p>目前如果直接nfs挂载根文件系统启动，会提示<strong>还缺少几个文件/etc/init.d/rcS /etc/fstab /etc/inittab</strong>，rcS 是个 shell 脚本，Linux 内核启动以后需要启动一些服务，而 <strong>rcS 就是规定启动哪些文件的脚本文件</strong>，<strong>fstab 在 Linux 开机以后自动配置哪些需要自动挂载的分区</strong>，<strong>inittab​定义系统启动、关闭、重启等不同阶段应该执行什么动作以及在哪个终端上执行</strong></p><p>最后就可以启动根文件系统，来测试是否构建成功了(可以通过运行c文件，测试添加开机自启动文件，测试网络，添加dns来测试)</p><p>当然还有其它的构建根文件系统的工具，比如<code>buildroot</code>，大家可以自行在网上查阅资料使用</p><h3 id="6-烧写系统"><a href="#6-烧写系统" class="headerlink" title="6. 烧写系统"></a>6. 烧写系统</h3><p>至此，三个部分，<strong>Uboot 内核 根文件系统</strong>，已经全部构建完成，就可以烧写整个系统到板子里开始学习linux驱动了，但是我们移植都是通过网络来测试的，在实际的产品开发中肯定不可能通过网络来运行，所以需要<strong>通过NXP 官方提供的 MfgTool 工具通过 USB OTG 口来烧写系统</strong>,具体方法也是修改官方的烧写脚本，在文件里面添加自己移植好的uboot，dtv，kernel，根文件系统，然后烧写到板子里。</p><h2 id="B-驱动概念相关"><a href="#B-驱动概念相关" class="headerlink" title="B 驱动概念相关"></a>B 驱动概念相关</h2><h3 id="1-Linux的内存管理"><a href="#1-Linux的内存管理" class="headerlink" title="1. Linux的内存管理"></a>1. Linux的内存管理</h3><p>在 32位 linux系统重，通常会把<strong>内存映射为4GB</strong>，通过<strong>内存管理单元 MMU</strong>实现虚拟和物理地址的转换</p><ul><li>程序所使用的内存地址叫做<strong>虚拟内存</strong>地址（Virtual Memory Address）</li><li>实际存在硬件里面的空间地址叫<strong>物理内存</strong>地址（Physical Memory Address）。</li></ul><p>这里面会用到<strong>页表</strong>—内存管理的核心数据结构之一，<strong>32位系统下通常使用2级表或3级表</strong></p><ul><li>页全局目录(PGD)</li><li>页中间目录(PMD)(可选)</li><li>页表(PT)</li></ul><blockquote><p><strong>驱动开发中如何通过物理地址得到虚拟地址</strong></p></blockquote><p>这里就涉及到了物理内存和虚拟内存之间的转换，需要用到两个函数<strong>ioremap</strong> 和  <strong>iounmap</strong>，当然如果使用设备树的可以使用 <strong>of_iomap</strong> 来直接获取设备节点对应的虚拟地址。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/io.h&gt;</span> <span class="comment">// 包含 ioremap 和 iounmap 的头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> __iomem *vaddr; <span class="comment">// 定义虚拟地址指针</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ioremap 的三个参数</span></span><br><span class="line"><span class="type">phys_addr_t</span> phys_addr = <span class="number">0x12340000</span>; <span class="comment">// 物理地址</span></span><br><span class="line"><span class="type">size_t</span> size = <span class="number">0x1000</span>;              <span class="comment">// 映射的大小（字节）</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> mtype = MT_DEVICE;    <span class="comment">// 映射类型，常用 MT_DEVICE</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 将物理地址映射为虚拟地址</span></span><br><span class="line">vaddr = ioremap_prot(phys_addr, size, mtype);</span><br><span class="line"><span class="keyword">if</span> (!vaddr) {</span><br><span class="line">    pr_err(<span class="string">"ioremap_prot failed\n"</span>);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用虚拟地址访问硬件寄存器</span></span><br><span class="line">iowrite32(<span class="number">0x12345678</span>, vaddr); <span class="comment">// 写入数据</span></span><br><span class="line">u32 value = ioread32(vaddr);  <span class="comment">// 读取数据</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 释放映射</span></span><br><span class="line">iounmap(vaddr);</span><br></pre></td></tr></table></figure></div><hr><blockquote><p><strong>虚拟内存的优缺点</strong></p></blockquote><ul><li><strong>优点</strong></li></ul><ol><li><p><strong>扩大地址空间</strong> 每个进程独占一个4G空间，让每个线程都认为自己有很大的内存，虽然真实物理内存没那么多</p></li><li><p><strong>内存保护</strong> 防止不同进程之间对物理内存的争夺，可以实现<strong>对特定内存实现读写保护</strong>，防止恶意篡改</p></li><li><p><strong>避免内存碎片</strong> 虽然物理地址可能不连续，但是映射虚拟地址上可以连续</p></li><li><p><strong>内存共享</strong> 可以实现进程间的通信，不同进程的虚拟地址可以指向同一个物理地址，实现内存共享(例如共享库)，节省内存空间</p></li></ol><ul><li><strong>缺点</strong></li></ul><ol><li><p>虚拟内存需要<strong>额外构建数据结构</strong>(<strong>页 页表 页框</strong> 分别对应物理内存 查询使用 虚拟内存)，占用空间</p></li><li><p><strong>增加执行时间</strong>，虚拟地址到物理地址的转化过程</p></li></ol><hr><blockquote><p><strong>​关于“申请虚拟内存不立即分配物理内存”​​</strong></p></blockquote><p>“只要不读写这个虚拟内存，操作系统就不会分配物理内存”——在原理上是正确的​<br>现代操作系统（如Linux）的malloc等函数在申请内存时，分配的是虚拟内存。操作系统仅在<strong>程序实际访问​（读取或写入）这块内存</strong>时，<strong>才会</strong>通过“缺页中断”机制为其<strong>分配真正的物理内存</strong>页，这个过程称为“按需分配”。</p><p>所以，单纯分配大量虚拟内存，理论上不会消耗对等的物理内存</p><hr><h3 id="2-字符设备驱动"><a href="#2-字符设备驱动" class="headerlink" title="2. 字符设备驱动"></a>2. 字符设备驱动</h3><p>linux系统将设备分为3类：<strong>字符设备、块设备、网络设备</strong><br>通常是应用层调用一些API函数，访问到内核里的驱动程序，然后再调用硬件外设，如下图</p><pre class="mermaid">flowchart TD    subgraph A [用户空间]        direction TB        A1[应用程序<br>open, close, read, write等API]        A2[库函数<br>C库等]    end    subgraph B [内核空间]        direction TB        B1[系统调用接口<br>sys_open, sys_close, sys_read, sys_write]        B2[驱动程序<br>open, close, read, release等函数]    end    C[硬件设备]    A1 --调用--&gt; A2    A2 --触发--&gt; B1    B1 --分发--&gt; B2    B2 --操作--&gt; C</pre><hr><blockquote><p><strong>字符设备驱动基础编写流程</strong>  </p></blockquote><ol><li><p><strong>模块入口与出口</strong>   module_init, module_exit  <strong>指定驱动加载和卸载时执行的函数</strong></p></li><li><p><strong>设备号申请</strong> ​ alloc_chrdev_region(<strong>动态</strong>) 或 register_chrdev_region(静态)</p></li><li><p><strong>初始化并注册 cdev</strong> (描述字符设备的结构体) cdev_init, cdev_add</p></li><li><p><strong>自动创建设备节点</strong> ​ class_create, device_create</p></li><li><p><strong>实现文件操作函数</strong> 定义并实现file_operations中的<strong>open, read, write, release</strong>等</p></li><li><p><strong>资源释放与注销</strong>   ​cdev_del, unregister_chrdev_region, device_destroy, class_destroy <strong>在模块退出时逆序释放所有资源</strong></p></li></ol><hr><h3 id="3-设备树"><a href="#3-设备树" class="headerlink" title="3. 设备树"></a>3. 设备树</h3><p>设备树(Device Tree)，将这个词分开就是“设备”和“树”，描述设备树的文件叫做 DTS(Device Tree Source)，这个 <strong>DTS 文件采用树形结构描述板级设备</strong>，也就是开发板上的设备信息，比如<strong>CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备</strong>等等</p><p>设备都是以节点的形式“挂”到设备树上的，因此要想获取这个设备的其他属性信息，必须<strong>先获取到这个设备的节点</strong>,。Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,<strong>OF函数</strong></p><p>下面演示一下获取设备节点和读取节点里的内容(不涉及错误处理)</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/of.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/of_device.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kernel.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">demo_get_device_node</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">device_node</span> *<span class="title">node</span>;</span></span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> *status;</span><br><span class="line">    u32 default_brightness;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取设备节点</span></span><br><span class="line">    node = of_find_node_by_path(<span class="string">"/backlight"</span>);</span><br><span class="line">    <span class="keyword">if</span> (!node)</span><br><span class="line">        <span class="keyword">return</span> -EINVAL;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取字符串属性</span></span><br><span class="line">    of_property_read_string(node, <span class="string">"status"</span>, &amp;status);</span><br><span class="line">    printk(<span class="string">"status: %s\n"</span>, status);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取单个 u32 属性</span></span><br><span class="line">    of_property_read_u32(node, <span class="string">"default-brightness-level"</span>, &amp;default_brightness);</span><br><span class="line">    printk(<span class="string">"default-brightness-level: %u\n"</span>, default_brightness);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>设备树中<strong>提供硬件连接和配置的静态描述</strong>，<strong>是pinctrl和GPIO子系统等配置信息的载体</strong></p><hr><h3 id="3-pinctrl-和-gpio-子系统实验"><a href="#3-pinctrl-和-gpio-子系统实验" class="headerlink" title="3. pinctrl 和 gpio 子系统实验"></a>3. pinctrl 和 gpio 子系统实验</h3><ul><li><strong>pinctrl 子系统</strong></li></ul><p>要使用 pinctrl 子系统，我们需要在设备树里面设置 PIN 的配置信息，然后pinctrl 子系统要根据你提供的信息来配置 PIN 功能，一般会<strong>在设备树里面创建一个节点来描述 PIN 的配置信息</strong></p><p>举个例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">pinctrl_test: testgrp {</span><br><span class="line">    fsl,pins = &lt;</span><br><span class="line">        MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config <span class="comment">/*config 是具体设置值*/</span></span><br><span class="line">    &gt;;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></div><p>通常会将这个节点放在iomuxc节点下，pinctrl 驱动程序是<strong>通过读取“fsl,pins”属性值来获取 PIN 的配置信息</strong></p><hr><ul><li><strong>GPIO 子系统</strong></li></ul><p>pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性，如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话，那么接下来就要用到 gpio 子系统了。<strong>gpio 子系统顾名思义，就是用于初始化 GPIO 并且提供相应的 API 函数，比如设置 GPIO为输入输出，读取 GPIO 的值等</strong>。gpio 子系统的主要目的就是方便驱动开发者使用 gpio，驱动开发者在设备树中添加 gpio 相关信息，然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO，Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程，极大的方便了驱动开发者使用 GPIO。</p><p>举个例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">test {</span><br><span class="line">    inctrl-names = <span class="string">"default"</span>;</span><br><span class="line">    pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_test&gt;;</span><br><span class="line">    gpio = &lt;&amp;gpio1 <span class="number">0</span> GPIO_ACTIVE_LOW&gt;;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></div><p>下面是一些常用API函数：<br><strong>gpio_request()</strong>      用于申请一个 GPIO 管脚<br><strong>gpio_free()</strong>         不使用某个 GPIO 了，通过该函数释放<br><strong>gpio_get_value()</strong>    用于获取某个 GPIO 的值(0 或 1)<br><strong>gpio_set_value()</strong>    用于设置某个 GPIO 的值</p><ul><li><strong>常见流程</strong></li></ul><ol><li><strong>添加 pinctrl 节点</strong>：设置 PIN 的复用和电气属性。</li><li><strong>添加设备节点</strong>：配置 GPIO 的输入、输出或中断功能。</li><li><strong>检查 PIN 冲突</strong>：确保 PIN 未被其他外设占用，避免资源冲突。</li></ol><hr><h3 id="4-处理并发与竞争的方法"><a href="#4-处理并发与竞争的方法" class="headerlink" title="4. 处理并发与竞争的方法"></a>4. 处理并发与竞争的方法</h3><p>Linux是一个多任务操作系统，肯定会存在多个任务共同操作同一段内存或者设备的情况，多个任务甚至中断都能访问的资源叫做共享资源，就和共享单车一样。<strong>在驱动开发中要注意对共享资源的保护，也就是要处理对共享资源的并发访问</strong></p><p>在linux下，引发竞争问题的原因主要有这几种:</p><ol><li><strong>多线程并发</strong>，多个线程同时操作某个文件</li><li><strong>抢占式并发</strong>，高优先级抢占访问了某个低优先级正在访问的文件</li><li><strong>中断式并发</strong>，多是硬件中断抢占访问了当前线程正在访问的资源</li><li><strong>多核（SMP）并发</strong>，多个核心同时访问某个资源</li></ol><ul><li>原子操作</li></ul><p>一般原子操作用于变量或者位操作，<strong>原子操作就是指不能再进一步分割的操作</strong>，在linux内核中通过 <strong>atomic_t</strong> 结构体来实现</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//原子整形变量</span></span><br><span class="line"><span class="type">atomic_t</span> v = ATOMIC_INIT(<span class="number">0</span>); <span class="comment">/* 定义并初始化原子变零 v=0 */</span></span><br><span class="line"><span class="type">atomic_set</span>(&amp;v, <span class="number">10</span>); <span class="comment">/* 设置 v=10 */</span></span><br><span class="line"><span class="type">atomic_read</span>(&amp;v); <span class="comment">/* 读取 v 的值，肯定是 10 */</span></span><br></pre></td></tr></table></figure></div><p>通过定义原子变量，保证变量修改的过程中不被其它资源访问<br><strong>位操作也是很常用的操作，Linux 内核也提供了一系列的原子位操作 API 函数，只不过原子位操作不像原子整形变量那样有个 atomic_t 的数据结构，原子位操作是直接对内存进行操作</strong><br>下面是一些常用API函数：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">set_bit</span><span class="params">(<span class="type">int</span> nr, <span class="type">void</span> *p)</span>;      <span class="comment">//将p地址的地nr位置1</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">clear_bit</span><span class="params">(<span class="type">int</span> nr,<span class="type">void</span> *p)</span>;     <span class="comment">//将p地址的地nr位清零</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">change_bit</span><span class="params">(<span class="type">int</span> nr, <span class="type">void</span> *p)</span>;   <span class="comment">//将p地址的nr位翻转</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">test_bit</span><span class="params">(<span class="type">int</span> nr, <span class="type">void</span> *p)</span>       <span class="comment">//返回p地址的第nr位</span></span><br><span class="line">......</span><br></pre></td></tr></table></figure></div><hr><ul><li><strong>自旋锁</strong></li></ul><p><strong>原子操作只能对整型变量或者位进行保护</strong>，而实际使用不可能只有这些简单的临界区，所以引入自旋锁<br>对于自旋锁而言，如果自旋锁<strong>正在被线程 A 持有，线程 B 想要获取自旋锁，那么线程 B 就会处于忙循环-旋转-等待状态，线程 B 不会进入休眠状态或者说去做其他的处理，而是会一直傻傻的在那里“转圈圈”的等待锁</strong><br>linux内核中使用 <strong>spinlock_t结构体</strong> 来表示自旋锁</p><p>下面是相关的API函数</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">spinlock_t</span> lock; <span class="comment">//首先定义自旋锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定义好自旋锁变量以后就可以使用相应的 API 函数来操作自旋锁</span></span><br><span class="line"></span><br><span class="line">DEFINE_SPINLOCK(<span class="type">spinlock_t</span> lock) <span class="comment">//定义并初始化一个自选变量。</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">spin_lock_init</span><span class="params">(<span class="type">spinlock_t</span> *lock)</span> <span class="comment">//初始化自旋锁。</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">spin_lock</span><span class="params">(<span class="type">spinlock_t</span> *lock)</span> <span class="comment">//获取指定的自旋锁，也叫做加锁。</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">spin_unlock</span><span class="params">(<span class="type">spinlock_t</span> *lock)</span> <span class="comment">//释放指定的自旋锁。</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">spin_trylock</span><span class="params">(<span class="type">spinlock_t</span> *lock)</span> <span class="comment">//尝试获取指定的自旋锁，如果没有获取到就返回 0</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">spin_is_locked</span><span class="params">(<span class="type">spinlock_t</span> *lock)</span>    <span class="comment">//检查指定的自旋锁是否被获取，如果没有被获取就返回非 0，否则返回 0。</span></span><br></pre></td></tr></table></figure></div><hr><p>下面介绍一个概念：<strong>死锁</strong></p><p>举几个例子：</p><ol><li>线程A获取自旋锁（此时<strong>自旋锁会自动禁止抢占</strong>），但是如果在线程A中却调用的一些<strong>休眠或者阻塞的函数，使得A主动放弃了CPU使用权</strong>，此时B<strong>线程开始运行了，如果B线程也想获取锁</strong>，可是锁在A线程，并且内核抢占也被关闭了，那B运行不了，A也无法运行，锁也无法释放，死锁便发生了</li><li>同理<strong>线程A获取自旋锁，此时中断发生了，抢走了CPU使用权，如果中断也想要自旋锁，中断就会一直自旋</strong>，那么A无法执行和释放锁，中断也无法继续执行，那么就会发生死锁</li><li><strong>A线程获取自旋锁，然后再临界区递归申请自旋锁1</strong>，那么线程A就会进入自旋，因为自己还没有释放锁，那么就会形成死锁</li></ol><p>如何解决这两种死锁:</p><ol><li>第一种就是<strong>杜绝在临界区执行休眠或者阻塞相关函数</strong></li><li>第二种就是<strong>在获取自旋锁之前，禁止本地中断</strong>，同时linux内核提供了相关API函数</li><li>第三种就是<strong>杜绝递归申请自旋锁</strong></li></ol><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">spin_lock_irq</span><span class="params">(<span class="type">spinlock_t</span> *lock)</span> 禁止本地中断，并获取自旋锁。</span><br><span class="line"><span class="type">void</span> <span class="title function_">spin_unlock_irq</span><span class="params">(<span class="type">spinlock_t</span> *lock)</span> 激活本地中断，并释放自旋锁。</span><br><span class="line"></span><br><span class="line"><span class="comment">//当然也有保存中断状态的API函数，但是不推荐使用</span></span><br></pre></td></tr></table></figure></div><hr><ul><li><strong>读写锁 顺序锁</strong></li></ul><table><thead><tr><th>锁类型</th><th>读并发</th><th>写并发</th><th>读写并发</th></tr></thead><tbody><tr><td>读写锁</td><td>可以</td><td>不可以</td><td>不可以</td></tr><tr><td>顺序锁</td><td>可以</td><td>不可以</td><td>可以</td></tr><tr><td>自旋锁</td><td>不可以</td><td>不可以</td><td>不可以</td></tr></tbody></table><hr><ul><li><strong>信号量 互斥体</strong></li></ul><p>这个在rt_thread中已经说明过，这里主要展示linux内核中相关API函数</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">semaphore</span> <span class="title">sem</span>;</span> <span class="comment">/* 定义信号量 */</span></span><br><span class="line">sema_init(&amp;sem, <span class="number">1</span>); <span class="comment">/* 初始化信号量 */</span></span><br><span class="line">down(&amp;sem); <span class="comment">/* 申请信号量 */</span></span><br><span class="line"><span class="comment">/* 临界区 */</span></span><br><span class="line">up(&amp;sem); <span class="comment">/* 释放信号量 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*---------------------------*/</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mutex</span> <span class="title">lock</span>;</span> <span class="comment">/* 定义一个互斥体 */</span></span><br><span class="line">mutex_init(&amp;lock); <span class="comment">/* 初始化互斥体 */</span></span><br><span class="line">mutex_lock(&amp;lock); <span class="comment">/* 上锁 */</span></span><br><span class="line"><span class="comment">/* 临界区 */</span></span><br><span class="line">mutex_unlock(&amp;lock); <span class="comment">/* 解锁 */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><hr><h3 id="5-Linux内核定时器"><a href="#5-Linux内核定时器" class="headerlink" title="5. Linux内核定时器"></a>5. Linux内核定时器</h3><p>定时器是我们最常用到的功能，一般用来完成定时功能，这里介绍Linux内核中定时器的使用方法，这里不展开系统节拍和时钟说明，主要说明在驱动中相关API函数如何使用</p><p><strong>Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数</strong>，系统启动的时候会将 jiffies 初始化为 0</p><p>内核定时器并<strong>不是周期性运行的，超时以后就会自动关闭，因此如果想要实现周期性定时，那么就需要在定时处理函数中重新开启定时器</strong>,Linux 内核使用 <strong>timer_list 结构体</strong>表示内核定时器</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">timer_list</span> {</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">entry</span>;</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> expires; <span class="comment">/* 定时器超时时间，单位是节拍数 */</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">tvec_base</span> *<span class="title">base</span>;</span></span><br><span class="line">    <span class="type">void</span> (*function)(<span class="type">unsigned</span> <span class="type">long</span>); <span class="comment">/* 定时处理函数 */</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> data; <span class="comment">/* 要传递给 function 函数的参数 */</span></span><br><span class="line">    <span class="type">int</span> slack;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></div><p>下面是一个完整例子，包含初始化定时器结构体，注册定时器，定时器中断每隔1s打印一次消息，打印10次后不再打印</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">timer_list</span> <span class="title">timer</span>;</span>    <span class="comment">//定义一个定时器</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定时器回调函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">function</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> arg)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> timer_k = <span class="number">0</span>;</span><br><span class="line">    timer_k++；</span><br><span class="line">    printk(<span class="string">"this is %d s\r\n"</span>,timer_k);</span><br><span class="line">    <span class="keyword">if</span>(timer_k&lt;=<span class="number">10</span>)</span><br><span class="line">    {</span><br><span class="line">        mod_timer(&amp;dev-&gt;timertest, jiffies + msecs_to_jiffies(<span class="number">1000</span>));   <span class="comment">//重新打开定时器，并计时1s</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    init_timer(&amp;timer);     <span class="comment">//初始化定时器</span></span><br><span class="line">    timer.function = function; <span class="comment">/* 设置定时处理函数 */</span></span><br><span class="line">    timer.expires=jffies + msecs_to_jiffies(<span class="number">1000</span>);<span class="comment">/* 超时时间 1 秒 */</span></span><br><span class="line">    timer.data = (<span class="type">unsigned</span> <span class="type">long</span>)&amp;dev; <span class="comment">/* 将设备结构体作为参数，当然这里没有用到*/</span></span><br><span class="line"></span><br><span class="line">    add_timer(&amp;timer);  <span class="comment">//启动定时器，1s后将会进入中断函数</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    del_timer(&amp;timer);  <span class="comment">//删除定时器</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//或者可以使用下面这个函数</span></span><br><span class="line">    <span class="comment">//del_timer_sync(&amp;timer);</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>上面个例子中，<strong>msecs_to_jiffies(1000)函数，可以将1000ms转化为jffies的单位，来实现符合s，ms的延时中断</strong></p><p>在不使用中断的时候，linux内核也提供短延时函数如下:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//分别为纳秒，微妙和毫秒延时</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">ndelay</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> nsecs)</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">udelay</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> usecs)</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mdelay</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> mseces)</span></span><br><span class="line">表</span><br></pre></td></tr></table></figure></div><hr><h3 id="6-Linux内核中断框架"><a href="#6-Linux内核中断框架" class="headerlink" title="6. Linux内核中断框架"></a>6. Linux内核中断框架</h3><p>中断都是频繁使用的功能之一，下面主要介绍中断的相关重点，比如<strong>中断号，中断上半部和下半部，以及设备树中如何申请</strong></p><ul><li><strong>中断号</strong></li></ul><p>每个中断都有一个中断号，<strong>通过中断号即可区分不同的中断</strong>，linux中用一个int的变量表示中断号，linux内核通过中断号找到对应的中断处理函数</p><p>申请中断号有两种:<br>一种是通过过 <strong>irq_of_parse_and_map</strong> 函数从设备树中的 <strong>interputs</strong> 属性中提取对应的设备号<br>如是使用GPIO的中断，可以使用 <strong>gpio_to_irq</strong> 函数来获取gpio对应的中断号</p><ul><li><strong>常用相关API函数</strong></li></ul><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//申请中断</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">request_irq</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> irq,       <span class="comment">//中断号</span></span></span><br><span class="line"><span class="params">                <span class="type">rq_handler_t</span> handler,   <span class="comment">//中断处理函数</span></span></span><br><span class="line"><span class="params">                <span class="type">unsigned</span> <span class="type">long</span> flags,    <span class="comment">//中断标志位(比如上升沿，下降沿等)</span></span></span><br><span class="line"><span class="params">                <span class="type">const</span> <span class="type">char</span> *name,       <span class="comment">//中断的名字，申请后可以在/proc/interrupts中看到</span></span></span><br><span class="line"><span class="params">                <span class="type">void</span> *dev)</span>              <span class="comment">//设备结构体，可以将改结构体传递给中断函数handler的第二个参数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//释放中断</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">free_irq</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> irq,     <span class="comment">//要释放的中断的中断号</span></span></span><br><span class="line"><span class="params">              <span class="type">void</span> *dev)</span>            <span class="comment">//传输的结构体</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//中断函数的格式，举个例子:</span></span><br><span class="line"><span class="type">static</span> <span class="type">irqreturn_t</span> <span class="title function_">key0_handler</span><span class="params">(<span class="type">int</span> irq,<span class="type">void</span>* dev_id)</span> <span class="comment">//分别是中断号和传递的结构体</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> value  = <span class="number">0</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">irq_key_desc</span>* <span class="title">dev</span> =</span> (<span class="keyword">struct</span> irq_key_desc*)dev_id;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**** 中断内容 ****/</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> IRQ_HANDLED;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//中断使能和禁止函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">enable_irq</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> irq)</span>       <span class="comment">//参数是中断号</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">disable_irq</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> irq)</span></span><br></pre></td></tr></table></figure></div><ul><li><strong>中断的上半部和下半部</strong></li></ul><p>我们之前使用<strong>request_irq()注册的中断函数就属于中断处理的上半部</strong>，只要中断触发，那么就会立即执行，而我们又知道中断必须快速执行，否则会影响整个系统，但现实中有些中断处理肯定很费时，所以这里引出下半部，举个例子：在stm32中中断函数里面通常设置一个标志位，然后在主函数循环里面读取这个标志位，再执行相关处理（这里就相当于是下半部）<br>总结一下:<br><strong>上半部</strong>：上半部就是中断处理函数，那些<strong>处理过程比较快，不会占用很长时间的处理就可以放在上半部完成</strong>。<br><strong>下半部</strong>：如果中断处理过程比较耗时，那么就将这些<strong>比较耗时的代码提出来，交给下半部去执行</strong>，这样中断处理函数就会快进快出</p><p>在linux中提供了很多下半部的机制:比如<strong>软中断，tasklet，工作队列，中断线程化</strong></p><p>其中tasklet就是基于软中断实现的，下面的介绍在gt911的介绍里面也讲过了，这里偷懒粘贴一下:</p><table><thead><tr><th>特性维度</th><th>软中断</th><th>Tasklet</th><th>工作队列</th><th>中断线程化</th></tr></thead><tbody><tr><td><strong>本质/关系</strong></td><td>传统下半部核心机制，基于中断上下文</td><td>基于软中断实现，更易用</td><td>传统下半部机制，运行于内核线程上下文</td><td>中断下半部的一种实现形式，但采用线程模型</td></tr><tr><td><strong>执行上下文</strong></td><td><strong>中断上下文</strong>（软中断上下文）</td><td><strong>中断上下文</strong>（软中断上下文）</td><td><strong>进程上下文</strong>（普通内核线程）</td><td><strong>进程上下文</strong>（专用实时内核线程）</td></tr><tr><td><strong>是否可阻塞/睡眠</strong></td><td><strong>否</strong></td><td><strong>否</strong></td><td><strong>是</strong></td><td><strong>是</strong></td></tr><tr><td><strong>优先级</strong></td><td>最高（在中断上下文执行）</td><td>较高</td><td>较低（普通线程调度）</td><td>可调度的实时优先级（通常高于普通工作队列）</td></tr><tr><td><strong>并发特性</strong></td><td>同一种类可在多CPU上<strong>并行</strong>，需重入设计</td><td>同一tasklet在多个CPU上<strong>串行</strong>执行</td><td>可配置，但同一工作队列中的任务可能串行执行</td><td>每个中断有<strong>独立线程</strong>，可绑定到特定CPU</td></tr><tr><td><strong>编程复杂度</strong></td><td>高（需考虑重入和并发安全）</td><td>中（自动序列化简化编程）</td><td>低（类似普通内核编程）</td><td>低（类似普通线程编程）</td></tr><tr><td><strong>适用场景</strong></td><td>高频、实时性要求极高的场景（如网络包处理）</td><td>简单的设备中断处理，需要避免并发问题</td><td>耗时操作，需要睡眠或阻塞的场景（如文件I/O）</td><td>复杂的驱动处理，需要实时性保证和可睡眠能力</td></tr></tbody></table><ul><li><strong>设备树相关</strong></li></ul><p>在imx6u上，intc节点下就是<strong>中断控制器节点</strong>，还有gpio也可以当做中断节点使用<br>举个例子:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">intc: interrupt-controller@<span class="number">00</span>a01000 {</span><br><span class="line">    compatible = <span class="string">"arm,cortex-a7-gic"</span>;</span><br><span class="line">    <span class="meta">#interrupt-cells = <span class="string">&lt;3&gt;</span>;</span></span><br><span class="line">    interrupt-controller;</span><br><span class="line">    reg = &lt;<span class="number">0x00a01000</span> <span class="number">0x1000</span>&gt;,</span><br><span class="line">          &lt;<span class="number">0x00a02000</span> <span class="number">0x100</span>&gt;;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">gpio5: gpio@<span class="number">020</span>ac000 { </span><br><span class="line">    compatible = <span class="string">"fsl,imx6ul-gpio"</span>, <span class="string">"fsl,imx35-gpio"</span>;   <span class="comment">//用于驱动匹配</span></span><br><span class="line">    reg = &lt;<span class="number">0x020ac000</span> <span class="number">0x4000</span>&gt;;                          <span class="comment">//寄存器的物理地址范围</span></span><br><span class="line">    interrupts = &lt;GIC_SPI <span class="number">74</span> IRQ_TYPE_LEVEL_HIGH&gt;,      <span class="comment">//声明中断信息</span></span><br><span class="line">                 &lt;GIC_SPI <span class="number">75</span> IRQ_TYPE_LEVEL_HIGH&gt;;  </span><br><span class="line">    gpio-controller;                                    <span class="comment">//声明这是一个GPIO控制器    </span></span><br><span class="line">    <span class="meta">#gpio-cells = <span class="string">&lt;2&gt;</span>;                                  <span class="comment">//规定引用该节点的参数&lt;引脚编号 标志&gt;</span></span></span><br><span class="line">    interrupt-controller;                               <span class="comment">//声明这是一个中断控制器</span></span><br><span class="line">    <span class="meta">#interrupt-cells = <span class="string">&lt;2&gt;</span>;                             <span class="comment">//规定引用该节点的参数&lt;引脚编号 触发方式&gt;</span></span></span><br><span class="line">     };</span><br></pre></td></tr></table></figure></div><p>然后就是在设备节点引用这个中断控制器，举个例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">gt911:gt911@<span class="number">5</span>d {</span><br><span class="line">  compatible = <span class="string">"goodix,gt911"</span>;      <span class="comment">//用于驱动匹配</span></span><br><span class="line">  reg = &lt;<span class="number">0x5d</span>&gt;;                     <span class="comment">//​​I2C 从设备地址</span></span><br><span class="line">  pinctrl-names = <span class="string">"default"</span>;        </span><br><span class="line">  pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_tsc         <span class="comment">//用的gpio引脚节点</span></span><br><span class="line">      &amp;pinctrl_tsc_reset &gt;;</span><br><span class="line">  interrupt-parent = &lt;&amp;gpio1&gt;;      <span class="comment">//中断父节点</span></span><br><span class="line">  interrupts = &lt;<span class="number">9</span> <span class="number">1</span>&gt;;               <span class="comment">/* 使用的是9号中断 因为引脚是9  触发方式是1 上升沿触发 */</span></span><br><span class="line">  reset-gpios  = &lt;&amp;gpio5 <span class="number">9</span> GPIO_ACTIVE_LOW&gt;;  <span class="comment">/* 4*32 + 9 = 128+9 = 137 */</span></span><br><span class="line">  interrupt-gpios = &lt;&amp;gpio1 <span class="number">9</span> GPIO_ACTIVE_LOW&gt;; <span class="comment">/* 9 */</span></span><br><span class="line">  status = <span class="string">"okay"</span>;</span><br><span class="line"> };</span><br></pre></td></tr></table></figure></div><hr><h3 id="7-阻塞-非阻塞IO"><a href="#7-阻塞-非阻塞IO" class="headerlink" title="7. 阻塞/非阻塞IO"></a>7. 阻塞/非阻塞IO</h3><p>这里的阻塞和非阻塞体现在是否进行休眠</p><ul><li><p><strong>阻塞</strong>: 用户态通过 read() 函数，然后进入内核态，<strong>如果设备不可用，那么就会进入休眠状态</strong>，阻塞到设备可用，然后返回数据给用户态</p></li><li><p><strong>非阻塞</strong>：用户态通过 read() 函数，然后进入内核态，<strong>如果设备不可用，那么直接返回错误码，这个时候用户态可以继续重新读取数据，如果还是不可用，就继续返回设备码</strong>，如果可用，就返回数据给用户态</p></li></ul><p>阻塞访问的优点在于<strong>如果文件不可操作的时候可以进入休眠状态，这样就可以让出CPU的资源</strong>，linux内核中通过<strong>等待队列</strong>来实现阻塞进程的唤醒工作，比如按键读取就可以使用阻塞IO访问</p><p>非阻塞访问的时候，<strong>linux内核通过轮询的方式来处理非阻塞访问</strong></p><hr><h3 id="8-异步通知"><a href="#8-异步通知" class="headerlink" title="8. 异步通知"></a>8. 异步通知</h3><p>之前的阻塞/非阻塞IO 是用户通过 read() 函数主动读取，对于非阻塞还是得不断地轮询，这并不是很好的处理方式。<br>所以<strong>这里引入异步通知，当驱动程序有消息的时候，主动向应用程序发出通知，报告自己可以访问，然后应用程序再读写文件</strong>，其实我们之前用的中断也是一种异步机制</p><p>阻塞、非阻塞、异步通知，这三种是针对不同的场合提出来的不同的解决方法，<strong>异步通知的核心是信号</strong>，Linux内核中定义了很多信号，比如:</p><div class="code-container" data-rel="H"><figure class="iseeu highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> SIGHUP 1 <span class="comment">/* 终端挂起或控制进程终止 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGINT 2 <span class="comment">/* 终端中断(Ctrl+C 组合键) */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGQUIT 3 <span class="comment">/* 终端退出(Ctrl+\组合键) */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGILL 4 <span class="comment">/* 非法指令 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGTRAP 5 <span class="comment">/* debug 使用，有断点指令产生 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGABRT 6 <span class="comment">/* 由 abort(3)发出的退出指令 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGIOT 6 <span class="comment">/* IOT 指令 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGBUS 7 <span class="comment">/* 总线错误 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGFPE 8 <span class="comment">/* 浮点运算错误 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGKILL 9 <span class="comment">/* 杀死、终止进程 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGUSR1 10 <span class="comment">/* 用户自定义信号 1 */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGSEGV 11 <span class="comment">/* 段违例(无效的内存段) */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGUSR2 12 <span class="comment">/* 用户自定义信号 2 */</span></span></span><br><span class="line"><span class="comment">/*......*/</span></span><br></pre></td></tr></table></figure></div><p>然后我们在应用程序中通过sighandler_t signa()函数来设置处理什么信号，设置相应的信号处理函数，比如<strong>SIGKILL信号</strong>，这个就是ctrl+c按下后内核发送的信号，下面用一个简单例子来演示:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//信号处理函数</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sigint_handler</span><span class="params">(<span class="type">int</span> num)</span></span><br><span class="line">{</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"\r\nSIGINT signal!\r\n"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    signal(SIGINT, sigint_handler); <span class="comment">//设置接收SIGINT信号，和信号处理函数</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后是驱动中的写法:</p><p>首先是 <strong>创建一个fasync_struct 结构体，然后再file_operations操作集中实现fasync函数</strong>，然后<strong>在fasync函数中通过调用fasync_helper来初始化之前创建的结构体</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">xxx_dev</span>{</span></span><br><span class="line">    ......</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">fasync_struct</span> *<span class="title">async_queue</span>;</span>  <span class="comment">//异步通信相关结构体</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">xxx_fasync</span><span class="params">(<span class="type">int</span>*fd,<span class="keyword">struct</span> file *filp,<span class="type">int</span> on)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">struct</span> xxx_dev *<span class="title function_">dev</span> <span class="params">(xxx_dev)</span>filp-&gt;private_data;</span><br><span class="line"></span><br><span class="line">    ret = fasync_helper(fd,filp,on,&amp;dev-&gt;async_queue);  <span class="comment">//初始化之前创建的结构体</span></span><br><span class="line">    <span class="keyword">if</span>( ret &lt; <span class="number">0</span>)</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">return</span> -EIO;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span> <span class="title">xxx_ops</span> =</span> {</span><br><span class="line">        ......</span><br><span class="line">        .fasync = xxx_fasync,   <span class="comment">//实现这个函数</span></span><br><span class="line">        ......</span><br><span class="line">    };</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//最后在release函数中删除异步通知</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">xxx_release</span><span class="params">(<span class="keyword">struct</span> inode *inode, <span class="keyword">struct</span> file *filp)</span></span><br><span class="line">{</span><br><span class="line">    <span class="keyword">return</span> xxx_fasync(<span class="number">-1</span>, filp, <span class="number">0</span>); <span class="comment">/* 删除异步通知 */</span></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>当初始化完之后，我们可以<strong>调用kill_fasync()函数发送指定的信号</strong>给用户</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">kill_fasync</span><span class="params">(<span class="keyword">struct</span> fasync_struct **fp, <span class="comment">//要操作的结构体</span></span></span><br><span class="line"><span class="params">                <span class="type">int</span> sig,                    <span class="comment">//要发送的信号</span></span></span><br><span class="line"><span class="params">                <span class="type">int</span> band)</span>                   <span class="comment">//可读时设置为 POLL_IN，可写时设置为 POLL_OUT</span></span><br></pre></td></tr></table></figure></div><p>在应用程序部分，对异步通知的处理要分成三步:</p><ol><li>使用 <code>signal</code> 函数来<strong>设置信号的处理函数</strong></li><li>使用 <code>fcntl(fd, F_SETOWN, getpid())</code> <strong>将本应用程序的进程号告诉内核</strong></li><li><strong>开启异步通知</strong>,首先通过<code>flags = fcntl(fd, F_GETFL);</code>来获取当前进程状态，然后通过<code>fcntl(fd, F_SETFL, flags | FASYNC);</code>来开启当前进程的异步通知功能，<strong>重点是通过fcntl函数设置进程状态为FASYNC</strong></li></ol><h3 id="9-Linux-IIC框架"><a href="#9-Linux-IIC框架" class="headerlink" title="9. Linux IIC框架"></a>9. Linux IIC框架</h3><p>首先要<strong>添加一个 i2c_client 结构体，是用来描述设备信息的</strong>，然后<strong>添加一个i2c_driver结构体</strong>，这是编写IIC驱动里面<strong>重点处理的内容，通常会有probe函数口和id匹配表等</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* i2c驱动结构体 */</span> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">i2c_driver</span> <span class="title">gt911_i2c_driver</span> =</span> {</span><br><span class="line">    .driver = {</span><br><span class="line">        .name  = <span class="string">"gt911"</span>,</span><br><span class="line">        .owner = THIS_MODULE,</span><br><span class="line">        .of_match_table = gt911_of_match_table,     <span class="comment">//设备树匹配表</span></span><br><span class="line">    },</span><br><span class="line">    .id_table = gt911_id_table,                     <span class="comment">//id匹配表</span></span><br><span class="line">    .probe  = gt911_probe,                          <span class="comment">//匹配成功后的入口函数</span></span><br><span class="line">    .remove = gt911_remove,                         <span class="comment">//出口</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></div><p>下面就是<strong>完善gt911_id_table，gt911_of_match_table，gt911_probe等</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_device_id</span> <span class="title">gt911_id_table</span>[] =</span> {</span><br><span class="line"> { <span class="string">"goodix,gt911"</span>, <span class="number">0</span>, },</span><br><span class="line">    { <span class="comment">/* sentinel */</span> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 设备树匹配表 </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">of_device_id</span> <span class="title">gt911_of_match_table</span>[] =</span> {</span><br><span class="line">    {.compatible = <span class="string">"goodix,gt911"</span> },</span><br><span class="line">    { <span class="comment">/* sentinel */</span> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">gt911_probe</span><span class="params">(<span class="keyword">struct</span> i2c_client *client, <span class="type">const</span> <span class="keyword">struct</span> i2c_device_id *id)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//在这里可以初始化GPIO，申请设备节点，申请中断等等</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">gt911_remove</span><span class="params">(<span class="keyword">struct</span> i2c_client *client)</span></span><br><span class="line">{</span><br><span class="line">    <span class="comment">//这里可以释放相关资源</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>通过上面这些函数，就可以完善IIC的框架，然后可以通过<strong>2c_add_driver函数来注册驱动</strong>，但是如果再注册驱动的上下文没有其它的事，就可以使用<strong>module_i2c_driver（​驱动模块的入口/出口辅助宏，用于常规驱动模块）</strong>,这个更方便</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">module_i2c_driver(gt911_i2c_driver);    <span class="comment">//当设备树/id匹配成功之后就可以进入probe函数了</span></span><br></pre></td></tr></table></figure></div><h3 id="10-Linux-Input子系统"><a href="#10-Linux-Input子系统" class="headerlink" title="10. Linux Input子系统"></a>10. Linux Input子系统</h3><p>/<em>待补充……</em>/</p><h2 id="C-QT有关驱动移植和根文件系统的构建"><a href="#C-QT有关驱动移植和根文件系统的构建" class="headerlink" title="C QT有关驱动移植和根文件系统的构建"></a>C QT有关驱动移植和根文件系统的构建</h2><h3 id="1-触摸屏-显示相关"><a href="#1-触摸屏-显示相关" class="headerlink" title="1.触摸屏/显示相关"></a>1.触摸屏/显示相关</h3><p>我这里使用的触摸屏ic引脚芯片是 <strong>gt911</strong> ,如果读者使用的触摸屏是其它芯片的可以看相关视频学习驱动<br>，gt911具体驱动内容可以看这篇文章：<a href="https://blog.haozi-haozi.cn/2025/09/26/embedded_imx6ull_gt911/">基于imx6ull的gt911移植</a></p><h3 id="2-支持QT的根文件系统的构建"><a href="#2-支持QT的根文件系统的构建" class="headerlink" title="2.支持QT的根文件系统的构建"></a>2.支持QT的根文件系统的构建</h3><p>具体教程可以参考文末的正点原子QT移植指南，这里简略介绍一下</p><ol><li><p>这里的环境搭建与正点原子驱动教程中的一致，都是 <strong>arm-poky-linux-gnueabi-gcc</strong> 编译器</p></li><li><p>然后是下载编译触摸插件: <strong>tslib</strong>,这个插件在编写gt911触摸屏驱动之后验证的过程中也已经使用过</p></li><li><p>下载并编译<strong>ARM平台</strong>下的QT5.12.9源码 （按照正点原子的教程可能会出现的报错）如下 <code>.Done. /home/mouse/Linux/tool/Qt-everywhere-src-5.12.9/qtbase/bin/qmake: 1: /home/mouse/Linux/tool/Qt-everywhere-src-5.12.9/qtbase/bin/qmake: Syntax error: word unexpected (expecting ")")</code> 解决办法是删掉解压的qt源码，然后重新解压再编译 (我的问题是这样解决的)</p></li><li><p>下一步是将编译好的tslib插件和QT移植到最初学习驱动构建的busybox文件系统中，其中tslib再gt911已经移植过，QT移植过程类似，都是打包，解压然后配置环境变量</p></li><li><p>最后连接显示屏然后执行QT测试文件，如果屏幕上显示qt的图片，并且能触摸，那么就大功告成啦！ <code>/usr/lib/arm-Qt/examples/widgets/animation/animatedtiles/animatedtiles</code></p></li><li><p>当然在后面制作自己的QT应用的时候，需要使用安装QT的交叉编译器，然后才能在Ubuntu下编译出能在IMX6u上运行的程序，这个正点原子的教程也有，我这里列出安装之后环境变量的默认路径 <code>/opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi</code>,后期使用的时候只用先是能环境变量，然后在QT工程目录下执行qmake即可生成Makefile文件，然后make生成最终文件，拷贝到开发板上就可以运行啦</p></li></ol><h3 id="3-基于QT的简易项目"><a href="#3-基于QT的简易项目" class="headerlink" title="3.基于QT的简易项目"></a>3.基于QT的简易项目</h3><p>这里就不贴源码了，因为其实比较简单，具体QT相关内容可以等我整理相关内容文章，后面是打算尝试整合这阵子学习的内容，做一个智能家居类(当然可能会添加功能，改成其它类型的项目，也是为了学习)的物联网项目 <a href="https://blog.haozi-haozi.cn/2025/10/12/embedded_imx6ull_smart_home_qt_tcp/">基于imx6u的智能家居</a></p><h2 id="D-编程相关概念-C语言-Linux"><a href="#D-编程相关概念-C语言-Linux" class="headerlink" title="D 编程相关概念(C语言 & Linux)"></a>D 编程相关概念(C语言 &amp; Linux)</h2><h3 id="1-C语言相关"><a href="#1-C语言相关" class="headerlink" title="1.C语言相关"></a>1.C语言相关</h3><hr><h4 id="1-1-空指针-野指针"><a href="#1-1-空指针-野指针" class="headerlink" title="1.1 空指针 & 野指针"></a>1.1 空指针 &amp; 野指针</h4><p><strong>空指针（Null Pointer）</strong> 它不指向任何有效的内存地址。在C语言中，我们通常<strong>使用 NULL 宏来表示空指针</strong>，它的值通常被定义为<strong>整数 0</strong>。空指针的概念很简单：它就是<strong>一个不指向任何地方的指针</strong>。</p><p>空指针的使用非常安全，因为大多数现代操作系统都会在程序试图访问空指针时立即终止程序，从而防止可能的数据损坏或安全漏洞。这种行为使得空指针成为检测指针是否有效的有用工具。<strong>当我们声明一个指针但还没有让它指向任何有效的内存地址时，最好将其初始化为空指针。</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> *ptr = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">if</span> (ptr == <span class="literal">NULL</span>) {</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"This is a null pointer\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p><strong>野指针（Dangling Pointer）</strong>,<strong>它是指向无效内存或者已被释放的内存的指针</strong>。与空指针不同，野指针指向的是一个<strong>随机</strong>的内存位置，这个位置可能包含任何数据，甚至可能是另一个正在运行的程序的数据。<strong>使用野指针可能导致程序崩溃、数据损坏或安全漏洞</strong>。</p><p>下面介绍五种产生野指针的场景:</p><p>1.<strong>指向临时变量的指针</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> *<span class="title function_">Ret_Dangling_P</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> arr[<span class="number">3</span>] = {<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>};</span><br><span class="line">    <span class="keyword">return</span> arr; <span class="comment">//这里返回的是局部变量arr数组的地址</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> * ptr = <span class="literal">NULL</span>;           <span class="comment">//空指针</span></span><br><span class="line">    ptr = Ret_Dangling_P();     <span class="comment">//这时变成了野指针</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol start="2"><li><strong>未初始化的指针</strong></li></ol><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line">    <span class="type">int</span> *ptr; <span class="comment">// 定义了一个指针，但未初始化</span></span><br><span class="line">    *ptr = <span class="number">10</span>; <span class="comment">// 直接使用未初始化的指针，可能指向随机内存地址，导致野指针</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol start="3"><li><strong>使用释放后的指针</strong></li></ol><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line">    <span class="type">int</span> *ptr = (<span class="type">int</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">int</span>)); <span class="comment">// 动态分配内存</span></span><br><span class="line">    *ptr = <span class="number">42</span>; <span class="comment">// 正常使用</span></span><br><span class="line">    <span class="built_in">free</span>(ptr); <span class="comment">// 释放内存</span></span><br><span class="line">    *ptr = <span class="number">10</span>; <span class="comment">// 此时 ptr 变成了野指针，指向已释放的内存</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol start="4"><li><strong>指针越界访问</strong></li></ol><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line">    <span class="type">int</span> arr[<span class="number">3</span>] = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line">    <span class="type">int</span> *ptr = arr;</span><br><span class="line">    ptr += <span class="number">5</span>; <span class="comment">// 指针越界，指向了数组范围之外的内存</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%d\n"</span>, *ptr); <span class="comment">// 访问越界内存，导致野指针</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol start="5"><li><strong>指针被多次释放</strong></li></ol><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line">    <span class="type">int</span> *ptr = (<span class="type">int</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">    <span class="built_in">free</span>(ptr); <span class="comment">// 第一次释放</span></span><br><span class="line">    <span class="built_in">free</span>(ptr); <span class="comment">// 再次释放，可能导致未定义行为</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><hr><h4 id="1-2-标识符-关键字-保留字"><a href="#1-2-标识符-关键字-保留字" class="headerlink" title="1.2 标识符 & 关键字 & 保留字"></a>1.2 标识符 &amp; 关键字 &amp; 保留字</h4><p><strong>标识符</strong>是指程序员在编程过程中<strong>用于命名变量、函数、类、模块或其他对象的名字</strong>。一个有效的标识符<strong>应该遵循特定的命名规则</strong>，具体规则可能因编程语言而异，比如<strong>不能以数字开头，不能与关键字相同等</strong></p><p><strong>关键字</strong>是指编程语言中<strong>具有特殊意义和用途的预定义单词</strong>，比如<strong>if else int等</strong>，由于关键字有特殊的含义，因此它们<strong>不能用作普通标识符</strong>。</p><p><strong>保留字</strong>是指在当前版本的编程语言中被保留但尚未使用的标识符，有的说明中会把它与关键字混为一谈，但实际还是不一样的。当然，<strong>它也不能当做普通标识符，比如做变量等</strong></p><hr><h4 id="1-3-static关键字"><a href="#1-3-static关键字" class="headerlink" title="1.3 static关键字"></a>1.3 static关键字</h4><p><strong>static 修饰的变量会放在静态区</strong>，而我们平常的<strong>局部变量等放在了栈区</strong>，动态申请的内存，比如<strong>malloc则放在堆区</strong></p><blockquote><p>1：在函数中声明变量时， <strong>static 关键字指定变量只初始化一次，并在之后调用该函数时保留其状态</strong>,比如在一个函数中记录进入该函数多少次<br>2️：在声明变量时，变量具有静态持续时间，并且除非您指定另一个值。<br>3：在全局和/或命名空间范围 (在单个文件范围内声明变量或函数时) static 关键字指定变量或函数为内部链接，即<strong>外部文件无法引用该变量或函数</strong>，通常在linux中声明函数，<strong>使得该函数只有当前文件可以访问</strong><br>4：static 关键字 没有赋值时，<strong>默认赋值为 0</strong><br>5：static修饰局部变量时，<strong>会改变局部变量的存储位置(静态区)</strong>，从而使得局部变量的生命周期变长，但作用域不变，比如函数里面static修饰的变量在主函数里面无法使用，同时<strong>不能用extern引用</strong></p></blockquote><hr><h4 id="1-4-volatile关键字"><a href="#1-4-volatile关键字" class="headerlink" title="1.4 volatile关键字"></a>1.4 volatile关键字</h4><p>volatile 关键字是一种类型修饰符，用<strong>它声明的类型变量表示可以被某些编译器未知的因素更改</strong>，比如：<strong>操作系统、硬件或者其它线程等</strong>。遇到这个关键字声明的变量，编译器对访问该变量的代码就<strong>不再进行优化</strong>，从而可以提供<strong>对特殊地址的稳定访问</strong>。</p><p>举个例子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">int</span> i = <span class="number">10</span>;</span><br><span class="line">    <span class="type">int</span> a = i;</span><br><span class="line"> </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"i = %d"</span>, a);</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 下面汇编语句的作用就是改变内存中 i 的值,改成32</span></span><br><span class="line">    <span class="comment">// 但是又不让编译器知道</span></span><br><span class="line">    __asm {</span><br><span class="line">        mov dword ptr [ebp<span class="number">-4</span>], <span class="number">20</span>h</span><br><span class="line">    }</span><br><span class="line"> </span><br><span class="line">    <span class="type">int</span> b = i;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"i = %d"</span>, b);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>如果这段代码在debug模式（即编译器不优化）下运行，那么结果是：</p><blockquote><p>i = 10<br>i = 32</p></blockquote><p>debug模式下这是正常结果，但是如果是release版本(编译器优化后版本)，那么结果可能是:</p><blockquote><p>i = 10<br>i = 10</p></blockquote><p>因为<strong>如果变量没有volatile修饰，那么编译器在优化的时候，可能不会去该变量的内存地址访问该变量，而是访问之前与该变量相等的变量(在这里就是a，因为之前写了a = i)，那么就可能会造成错误的结果</strong></p><p>在嵌入式开发，或者多线程的linux开发中，尝尝会有很多线程任务同时访问同一个变量，那么为了防止被编译器优化，会使用该关键字</p><p>同理，<strong>volatile也可以修饰指针，指向易变数据的指针</strong>，这时如果有任务通过该指针访问变量的时候，也会<strong>直接通过内存地址访问，而不会使用编译器事先缓存的值</strong></p><p>应用时，<strong>在嵌入式里，通常会用volatile修饰一些寄存器地址，然后我们通过宏-&gt;地址修改变量的时候，也会直接访问，而不会被优化</strong></p><p>c++部分<br>volatile关键字除了修饰变量和指针，也能用来修饰类的成员函数。这表示该成员函数可以被 ​volatile对象调用，并且在函数内部对待对象的成员变量时，会遵循 volatile的语义（即每次访问都从内存中读取最新值，而不是使用可能被编译器缓存的副本）</p><hr><h4 id="1-5-const关键字"><a href="#1-5-const关键字" class="headerlink" title="1.5 const关键字"></a>1.5 const关键字</h4><p>关键字<strong>const用来定义常量</strong>，如果一个变量被const修饰，那么它的<strong>值就不能再被改变</strong></p><p>与define的不同在于:</p><ol><li>预编译指令只是对值进行简单的替换，<strong>不能进行类型检查</strong></li><li>可以<strong>保护被修饰的东西</strong>，防止意外修改，增强程序的健壮性</li><li>编译器通常不为普通const常量分配存储空间，而是将它们保存在符号表中，这使得它成为一个编译期间的常量，没有了存储与读内存的操作，使得它的效率也很高。</li></ol><ul><li><strong>常量指针 &amp; 指针常量</strong></li></ul><p>常量指针是指<strong>不能通过该指针修改指向的变量，但是可以通过其它途径修改这个变量</strong>，同时这个指针是可以被修改的。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="type">int</span> b = <span class="number">2</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> *ptr = &amp;a;    <span class="comment">//常量指针</span></span><br><span class="line">a = <span class="number">3</span>;                  <span class="comment">//可以通过其它途径修改这个变量</span></span><br><span class="line">ptr = &amp;b;               <span class="comment">//指针是可以被修改</span></span><br></pre></td></tr></table></figure></div><p>指针常量是指 <strong>指针自己不能被修改</strong>，但<strong>指向的内容可以被修改</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="type">int</span> * <span class="type">const</span> ptr = &amp;a;   <span class="comment">//指针常量</span></span><br><span class="line">*ptr = <span class="number">2</span>;               <span class="comment">//可以修改指向的内容</span></span><br></pre></td></tr></table></figure></div><p>当然还有套娃的 指向常量的常指针: <strong>即不能修改指针本身，也不能修改指针指向的变量</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span>* cosnt ptr = &amp;a;      <span class="comment">//指向常量的常指针</span></span><br></pre></td></tr></table></figure></div><ul><li><strong>cosnt修饰函数参数</strong></li></ul><p>和之前介绍的三种情况一样，也是为了在函数中 <strong>防止修改指针</strong> 或 <strong>防止修改指针指向的变量</strong> 或 <strong>二者都要</strong></p><ul><li><strong>cosnt修饰函数返回值</strong></li></ul><p>如果给以“指针传递”方式的函数返回值加 const 修饰，那么<strong>函数返回值（即指针）的内容不能被修改</strong>，该返回值<strong>只能被赋给加const 修饰的同类型指针</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> * <span class="title function_">Fun</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">const</span> <span class="type">int</span>* ptr1 = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="type">int</span>* ptr2 = <span class="literal">NULL</span>;</span><br><span class="line">    ptr1 = Fun();           <span class="comment">//正确使用方法</span></span><br><span class="line">    ptr2 = Fun();           <span class="comment">//错误使用方法，会报错，因为ptr2没有使用const修饰</span></span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><ul><li><strong>修饰全局变量</strong></li></ul><p>修饰后的全局变量一半存储在只读数据段，局部变量一般在栈区，也有可能被编译器优化</p><p>修饰全局变量是为了<strong>防止程序其他部分无意或恶意地修改该全局变量，从而保护数据的完整性，提高程序的健壮性</strong></p><hr><h4 id="1-6-使用头文件时双引号和尖括号的区别是什么"><a href="#1-6-使用头文件时双引号和尖括号的区别是什么" class="headerlink" title="1.6 使用头文件时双引号和尖括号的区别是什么?"></a>1.6 使用头文件时双引号和尖括号的区别是什么?</h4><p>使用<strong>双引号</strong>，会<strong>先在当前项目路径下查找该头文件</strong>，如果查不到，才会去系统目录下查找<br>使用<strong>尖括号</strong>，会<strong>直接去系统目录下查找该文文件</strong></p><hr><h4 id="1-7-输出重定向"><a href="#1-7-输出重定向" class="headerlink" title="1.7 输出重定向"></a>1.7 输出重定向</h4><p>输出重定向是指把程序的输出，除了输出在屏幕上以外的另外选择， 比如说，输出到一个文件里<br>在嵌入式里，<strong>比如重定向到串口的发送函数</strong>中，这样就可以通过printf直接向串口发送消息调试</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//重定义fputc函数 </span></span><br><span class="line"><span class="type">int</span> <span class="title function_">fputc</span><span class="params">(<span class="type">int</span> ch, FILE *f)</span></span><br><span class="line">{      </span><br><span class="line">    <span class="keyword">while</span>((USART1-&gt;SR &amp; <span class="number">0X40</span>)==<span class="number">0</span>); <span class="comment">//循环发送,直到发送完毕   </span></span><br><span class="line">    USART1-&gt;DR = (u8) ch;      </span><br><span class="line">    <span class="keyword">return</span> ch;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>​链接器的行为: 许多C标准库（如ARM的MicroLib或某些Newlib配置）会将 fputc定义为<strong>弱符号</strong>（weak symbol）​。这意味着如果您在工程中自己定义了一个<strong>同名的强符号函数，链接器会优先使用您的实现</strong>，从而**“覆盖”**库中的默认版本。这是重定向能够实现的底层机制。</p><p>​printf的依赖链: 当程序调用 printf时，<strong>printf会解析格式字符串并最终多次调用 fputc来输出每一个字符</strong>。当我们重定义了 fputc，这些字符就<strong>会被发送到串口，而不是标准的输出设备</strong>。</p><hr><h3 id="2-Linux相关"><a href="#2-Linux相关" class="headerlink" title="2.Linux相关"></a>2.Linux相关</h3><h4 id="2-1-如何通过结构体的成员，来获得该结构体的指针"><a href="#2-1-如何通过结构体的成员，来获得该结构体的指针" class="headerlink" title="2.1 如何通过结构体的成员，来获得该结构体的指针"></a>2.1 如何通过结构体的成员，来获得该结构体的指针</h4><p>使用 <strong>container_of</strong> 宏，内核函数调用常常给函数传入的是结构体成员地址，然后在函数里面又想使用这个结构体里面的其他成员变量，所以就引发了这样的问题，这个宏用来解决这个问题。</p><p><strong>定义</strong>：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> container_of(ptr, type, member) \</span></span><br><span class="line"><span class="meta">    ((type *)((char *)(ptr) - (char *)(&amp;((type *)0)-&gt;member)))</span></span><br></pre></td></tr></table></figure></div><p><strong>使用方法</strong>：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">container_of(ptr, type, member);</span><br></pre></td></tr></table></figure></div><ul><li><code>ptr</code>：指向结构体成员的指针。</li><li><code>type</code>：包含该成员的结构体类型。</li><li><code>member</code>：结构体中的成员名称。</li></ul><p><strong>原理</strong>：<br><code>container_of</code> <strong>宏的核心思想是通过成员变量的地址，反推出整个结构体的起始地址。它利用了结构体成员在内存中的偏移量</strong>（<code>offsetof</code>），通过指针运算实现。</p><p><strong>步骤解析</strong>：</p><ol><li><code>(type *)0</code>：将 0 转换为 <code>type *</code> 类型，表示一个虚拟的结构体指针。</li><li><code>&amp;((type *)0)-&gt;member</code>：通过虚拟指针访问 <code>member</code>，得到该成员相对于结构体起始地址的偏移量。（<strong>通过这个虚拟的指针去访问结构体中的成员 member。编译器在编译时，会根据结构体 type的内存布局规则​（如内存对齐）来确定 member的位置</strong>）</li><li><code>(char *)(ptr) - (char *)(&amp;((type *)0)-&gt;member)</code>：将传入的成员指针 <code>ptr</code> 减去偏移量，得到结构体的起始地址。</li><li><code>(type *)</code>：将结果转换为 <code>type *</code> 类型，返回结构体的指针。</li></ol><p><strong>示例代码</strong>：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stddef.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个结构体</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">example</span> {</span></span><br><span class="line">    <span class="type">int</span> a;</span><br><span class="line">    <span class="type">double</span> b;</span><br><span class="line">    <span class="type">char</span> c;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">example</span> <span class="title">ex</span> =</span> {<span class="number">1</span>, <span class="number">3.14</span>, <span class="string">'x'</span>};</span><br><span class="line">    <span class="type">char</span> *member_ptr = &amp;ex.c; <span class="comment">// 获取成员 c 的地址</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 container_of 宏获取结构体指针</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">example</span> *<span class="title">struct_ptr</span> =</span> container_of(member_ptr, <span class="keyword">struct</span> example, c);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 验证结果</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Original struct address: %p\n"</span>, (<span class="type">void</span> *)&amp;ex);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Calculated struct address: %p\n"</span>, (<span class="type">void</span> *)struct_ptr);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><strong>输出</strong>：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Original <span class="class"><span class="keyword">struct</span> <span class="title">address</span>:</span> <span class="number">0x7ffee4b8c8f0</span></span><br><span class="line">Calculated <span class="class"><span class="keyword">struct</span> <span class="title">address</span>:</span> <span class="number">0x7ffee4b8c8f0</span></span><br></pre></td></tr></table></figure></div><p><code>container_of</code> 宏是 Linux 内核中非常重要的工具，广泛用于从结构体成员指针获取整个结构体指针。它的实现依赖于<strong>指针运算和偏移量计算</strong>，避免了显式的类型转换和冗余代码，提高了代码的可读性和可维护性。</p><hr><h4 id="2-2-什么时候从用户态进入内核态"><a href="#2-2-什么时候从用户态进入内核态" class="headerlink" title="2.2 什么时候从用户态进入内核态"></a>2.2 什么时候从用户态进入内核态</h4><p>其中，系统调用是主动的，另外两种是被动的。</p><p>a、<strong>系统调用</strong> （直接调用系统接口或通过库函数调用）<br>b、<strong>异常</strong>     （捕捉信号处理异常 signal）<br>c、<strong>设备中断</strong></p><hr><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><a class="link" href="http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html">【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://gitee.com/GuangzhouXingyi/imx6ull-document/blob/master/%E3%80%90%E6%AD%A3%E7%82%B9%E5%8E%9F%E5%AD%90%E3%80%91I.MX6U%20%E7%A7%BB%E6%A4%8DQt5.12.9%20V1.1.pdf">【正点原子】I.MX6U 移植Qt5.12.9 V1.1<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#52;&#x36;&#x37;&#48;&#x32;&#51;&#54;&#50;&#x40;&#113;&#113;&#x2e;&#x63;&#111;&#x6d;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">主要记录嵌入式Linux驱动学习和QT相关移植以及C语言重点复习,主要将非实验，即概念性的内容在这里记录，方便以后查看</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/categories/Embedded/Linux/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="Qt" scheme="https://blog.haozi-haozi.cn/tags/Qt/"/>
    
    <category term="Linux" scheme="https://blog.haozi-haozi.cn/tags/Linux/"/>
    
    <category term="i.MX6ULL" scheme="https://blog.haozi-haozi.cn/tags/i-MX6ULL/"/>
    
    <category term="C" scheme="https://blog.haozi-haozi.cn/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>MSPM0G3507-地猛星-环境配置</title>
    <link href="https://blog.haozi-haozi.cn/2025/06/11/embedded_MSPM0G3507_CCS/"/>
    <id>https://blog.haozi-haozi.cn/2025/06/11/embedded_MSPM0G3507_CCS/</id>
    <published>2025-06-11T12:30:55.000Z</published>
    <updated>2026-03-24T08:58:23.297Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-引入"><a href="#A-引入" class="headerlink" title="A 引入"></a>A 引入</h2><h2 id="A-1-板子和环境说明"><a href="#A-1-板子和环境说明" class="headerlink" title="A.1 板子和环境说明"></a>A.1 板子和环境说明</h2><ul><li>给电赛备赛学习的TI板，留下笔记方便查看<br>使用的板子型号是嘉立创的<strong>地猛星</strong>MSPM0G3507<br>环境是windows11,因为听说CCS是基于VsCode，所以选择使用CCS进行开发</li></ul><h2 id="B-CCS下载"><a href="#B-CCS下载" class="headerlink" title="B CCS下载"></a>B CCS下载</h2><h3 id="B-1-官网地址"><a href="#B-1-官网地址" class="headerlink" title="B.1 官网地址"></a>B.1 官网地址</h3><p><a class="link" href="https://www.ti.com.cn/">TI中文官网地址<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a><br>打开之后需要注册/登录账号然后就能看到搜索框，然后再搜索框中输入CCS回车</p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_042.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_043.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_044.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_045.webp" width="500"><p>  下载之后直接安装即可（其中有一步是让选择自己使用的板子型号，我们选择MSPM0即可，按照自己的需求选择，后面也可以继续添加<br>  <strong>注意所有对CCS的文件操作必须在英文路径下，如果是中文则会出现不同的报错</strong></p><h3 id="B-2-汉化配置"><a href="#B-2-汉化配置" class="headerlink" title="B.2 汉化配置"></a>B.2 汉化配置</h3><p>软件安装之后打开默认为英文，配置中文的步骤很简单</p><ol><li>按下快捷键 Ctrl+Shift+P调出命令行</li><li>输入 dsp</li><li>选择 Configure Display Language 然后回车</li><li>等待加载选项，然后选择中文(简体)即可，最后软件重启一下就是中文了</li></ol><h2 id="C-新建工程"><a href="#C-新建工程" class="headerlink" title="C 新建工程"></a>C 新建工程</h2><h3 id="C-1-新建一个空的工程"><a href="#C-1-新建一个空的工程" class="headerlink" title="C.1 新建一个空的工程"></a>C.1 新建一个空的工程</h3><p>新建的方法和STM32CubeMX的方法非常类似<br>举个例子：下面是MSPM0G3537的创建方法:</p><ol><li>点击最上方的 文件 - Create New Project…</li><li>然后按照下方图片选择</li></ol>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_046.webp" width="500"><ol start="3"><li><p>点击之后可能会提示你下载两个东西，一个是SDK包，一个是图形化工具，直接点击下载就行，可能会有点慢，失败了大部分是网速问题，换个网络多试几次就行</p></li><li><p>下载完之后生成出来的界面如下，然后如果是地猛星使用前要改一下封装</p></li></ol>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_047.webp" width="500"><h3 id="C-2-点灯"><a href="#C-2-点灯" class="headerlink" title="C.2 点灯"></a>C.2 点灯</h3><p>地猛星的自带LED是PA14,下面是配置流程</p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_048.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_049.webp" width="500"><p>配置完成之后按 Ctrl + S 保存配置，然后右键工程文件夹，点击 Build Project(s) 编译一下工程就会生成相关的配置文件，最后我们只用在 empty.c 文件的main函数中写相关驱动代码即可</p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_050.webp" width="500"><p>在 Debug - ti_msp_dl_config.h文件中会有我们刚刚初始化LED相关IO口的宏定义</p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_050.webp" width="500"><p>最后我们在主函数中写下翻转电平的代码,然后重新编译工程就ok了，下面介绍如何串口烧录（因为地猛星自带一个Type-C接口可以直接连接电脑，主要是没买J-Link）</p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_052.webp" width="500"><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ti_msp_dl_config.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{</span><br><span class="line">    SYSCFG_DL_init();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>) {</span><br><span class="line">    DL_GPIO_togglePins(LED1_PORT,LED1_PIN_14_PIN);  <span class="comment">//电平翻转</span></span><br><span class="line">    delay_cycles(<span class="number">32000000</span>);                 <span class="comment">//延时</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h2 id="D-串口烧录配置"><a href="#D-串口烧录配置" class="headerlink" title="D 串口烧录配置"></a>D 串口烧录配置</h2><h3 id="D-1-设置CCS输出Bin文件"><a href="#D-1-设置CCS输出Bin文件" class="headerlink" title="D.1 设置CCS输出Bin文件"></a>D.1 设置CCS输出Bin文件</h3><ol><li>右键工程文件，选择 <strong>Properties…</strong></li><li>根据下方操作，配置CCS生成bin文件</li></ol>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_053.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_054.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_055.webp" width="500"><ol start="3"><li><p>全部配置完成后点击保存配置，然后重新编译整个工程，会发现在Debug文件夹下生成了一个hex文件，如果当时工程名是默认的，那么可能文件名为:empty_LP_MSPM0G3507_nortos_ticlang.hex</p></li><li><p>然后CCS的工作就结束了</p></li></ol><h3 id="D-2-UniFlash下载和使用"><a href="#D-2-UniFlash下载和使用" class="headerlink" title="D.2 UniFlash下载和使用"></a>D.2 UniFlash下载和使用</h3><h3 id="D-2-1-官网软件下载"><a href="#D-2-1-官网软件下载" class="headerlink" title="D.2.1 官网软件下载"></a>D.2.1 官网软件下载</h3><p>直接官网下载安装即可，还是尽量全英文<br><a class="link" href="https://www.ti.com/tool/UNIFLASH">UniFlash官方下载链接<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a><br>和前面下载CCS的方法基本一致，这里就不说了<br>安装过程都默认即可</p><h3 id="D-2-2-烧录配置"><a href="#D-2-2-烧录配置" class="headerlink" title="D.2.2 烧录配置"></a>D.2.2 烧录配置</h3><ol><li>打开软件，在主页面选择自己的板子型号，要注意的是要选择后方有Serial标注的那个，否则就并非是串口烧录,最后点击Start</li></ol>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_056.webp" width="500"><ol start="2"><li>这里选择刚刚生成的hex文件，然后下面的串口必须选择和板子连接的串口，我这里是COM10,再下面的波特率看自己情况选(115200即可，可以在设备管理器里面查看具体的端口号，如果很多不知道选择哪个，插拔一下就知道是哪个了)<strong>注意要安装CH340驱动</strong>，没有的可以自行上网查找资源</li></ol>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_057.webp" width="500"><ol start="3"><li><p>这里还有一步是配置开发板为下载模式：在开发板保持通电的情况下，一直按住BSL，然后按一下RST键大概1秒，松开RST键，就可以正常进入升级模式。这时也可以松开BSL键。</p></li><li><p>如果都配置好了，最后回到uniflash中，点<strong>Load image</strong>即可下载。</p></li><li><p>当出现了一个报错：Image loading falled: Try manual Bootloader invocation…</p></li></ol><p>那么代码实际上已经烧录成功了，如果出现其它报错可以参考<br><a class="link" href="https://wiki.lckfb.com/zh-hans/dmx/question/uart-download-error.html">报错问题合集<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a><br><a class="link" href="https://wiki.lckfb.com/zh-hans/dmx/question/uart-download-error.html">嘉立创串口下载教程<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><h2 id="E-其它烧录配置"><a href="#E-其它烧录配置" class="headerlink" title="E 其它烧录配置"></a>E 其它烧录配置</h2><h3 id="E-1-烧录方式选择"><a href="#E-1-烧录方式选择" class="headerlink" title="E.1 烧录方式选择"></a>E.1 烧录方式选择</h3><p>右键工程文件，选择 <strong>Properties…</strong>，然后在<strong>Connection</strong>中可以选择连接方式，比如选择J-Link之后，你需要J-link的相关驱动，然后点击上方工具栏的运行-Debug 就可以烧录调试了</p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_058.webp" width="500"><blockquote><p>嘉立创官方文档中比较推荐使用J-Link，因为串口烧录会有一些小问题好像</p></blockquote><h4 id="欧克，环境配置到此结束"><a href="#欧克，环境配置到此结束" class="headerlink" title="欧克，环境配置到此结束"></a>欧克，环境配置到此结束</h4><h2 id="F-文件管理–（分文件，仿江科大和正点原子的stm32文件格式）"><a href="#F-文件管理–（分文件，仿江科大和正点原子的stm32文件格式）" class="headerlink" title="F 文件管理–（分文件，仿江科大和正点原子的stm32文件格式）"></a>F 文件管理–（分文件，仿江科大和正点原子的stm32文件格式）</h2><h3 id="F-1-创建成功工程之后，点击左上角文件，另存为工作区或者保存工作区"><a href="#F-1-创建成功工程之后，点击左上角文件，另存为工作区或者保存工作区" class="headerlink" title="F.1 创建成功工程之后，点击左上角文件，另存为工作区或者保存工作区"></a>F.1 创建成功工程之后，点击左上角文件，另存为工作区或者保存工作区</h3>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_059.webp" width="500"><p><strong>需保证出现如下工作区样式</strong></p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_060.webp" width="500"><p>然后右键工程，Properties.. - Arm Compiler - Include Options 添加自己的驱动文件路径，注意使用绝对路径**${PROJECT _ROOT}**</p>  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_061.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_062.webp" width="500">  <img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_062.webp" width="500"><p>最后正常包头文件，编译就不会报错了</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><p>1.<a class="link" href="https://wiki.lckfb.com/zh-hans/ti-series/">嘉立创地猛星相关资料<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p></blockquote><h3 id="文件地址"><a href="#文件地址" class="headerlink" title="文件地址"></a>文件地址</h3><blockquote><p>官网基本都有，速度也还不错</p></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#52;&#x36;&#x37;&#48;&#x32;&#51;&#54;&#x32;&#64;&#113;&#113;&#46;&#99;&#111;&#x6d;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">TI板-MSPM0G3507-天空星-环境配置-CCS和串口烧录</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="MSPM0" scheme="https://blog.haozi-haozi.cn/tags/MSPM0/"/>
    
    <category term="CCS" scheme="https://blog.haozi-haozi.cn/tags/CCS/"/>
    
  </entry>
  
  <entry>
    <title>常用的通信协议总结(含Ethernet 基础)</title>
    <link href="https://blog.haozi-haozi.cn/2025/05/03/embedded_Communication%20Protocol/"/>
    <id>https://blog.haozi-haozi.cn/2025/05/03/embedded_Communication%20Protocol/</id>
    <published>2025-05-03T03:22:45.000Z</published>
    <updated>2026-03-24T08:57:34.939Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-概念相关"><a href="#A-概念相关" class="headerlink" title="A 概念相关"></a>A 概念相关</h2><h3 id="1-并行通信-串行通信"><a href="#1-并行通信-串行通信" class="headerlink" title="1. 并行通信 & 串行通信"></a>1. 并行通信 &amp; 串行通信</h3><ul><li><strong>并行通信</strong> 在同一时刻发送多位数据，优点是速度快，但是传输距离短(长距离信号同步困难，干扰大)，通常要多根线</li><li><strong>串行通信</strong> 逐位顺序传输​数据，优点是布线简单，距离远，差发信号时抗干扰能力强</li></ul><h3 id="2-全双工-半双工-单工"><a href="#2-全双工-半双工-单工" class="headerlink" title="2. 全双工 & 半双工 & 单工"></a>2. 全双工 &amp; 半双工 &amp; 单工</h3><ul><li><strong>单工</strong> 只能接受或者发送。例如：收音机、遥控器等，一般只有一根线</li><li><strong>全双工</strong> 在同一时刻只能发送或者接收。例如：对讲机，至少有两根线；</li><li><strong>半双工</strong> 在同一时刻既能接收又能发送。例如：电话，至少有两根线。</li></ul><h3 id="3-同步通信-异步通信"><a href="#3-同步通信-异步通信" class="headerlink" title="3. 同步通信 & 异步通信"></a>3. 同步通信 &amp; 异步通信</h3><ul><li><strong>同步通信</strong> <strong>需要时钟线同步信号</strong>，例如IIC和SPI等,其中USART(通用同步异步串行收发器),支持同步通信和异步通信</li><li><strong>异步通信</strong> 事先约定好大致的速度（波特率),例如串口(UART)(通用异步收发器)</li></ul><h2 id="B-常见硬件通信协议"><a href="#B-常见硬件通信协议" class="headerlink" title="B 常见硬件通信协议"></a>B 常见硬件通信协议</h2><h3 id="1-UART协议"><a href="#1-UART协议" class="headerlink" title="1. UART协议"></a>1. UART协议</h3><p>通过RX TX 两根线实现异步<strong>全双工通信</strong>，不需用共享时钟信号，只需用根据<strong>事先约定好的数据格式和波特率</strong>即可正常工作</p><blockquote><p>关键参数： <strong>波特率 停止位 数据位 校验位</strong></p></blockquote><p>一个完整的数据帧通常由以下部分组成<br><strong>起始位+数据位+校验位+停止位</strong> 起始位为低电平(0)，停止位为高电平(1)，数据位一般为8位，传输顺序是<strong>低位 (LSB) 先发​</strong>，校验位（可选）通常为奇校验/偶校验</p><blockquote><p>电平标准：信号在物理介质上的表示</p></blockquote><table><thead><tr><th align="left">电平标准</th><th align="left">逻辑 0</th><th align="left">逻辑 1</th><th align="left">特点与用途</th></tr></thead><tbody><tr><td align="left"><strong>TTL/CMOS</strong></td><td align="left">0V (或 &lt;0.8V)</td><td align="left">3.3V 或 5V (&gt;2.4V)</td><td align="left">板内短距离通信（&lt;1米），如MCU之间、MCU与传感器模块之间。</td></tr><tr><td align="left"><strong>RS-232</strong></td><td align="left">+3V ~ +15V <strong>(正电压)</strong></td><td align="left">-3V ~ -15V <strong>(负电压)</strong></td><td align="left">抗干扰能力强，支持较长距离（约15米）。<strong>注意：逻辑定义与TTL相反</strong>。</td></tr><tr><td align="left"><strong>RS-485</strong></td><td align="left">A线电压 &gt; B线电压 <strong>(差分, A+ B-)</strong></td><td align="left">B线电压 &gt; A线电压 <strong>(差分, A- B+)</strong></td><td align="left">差分信号，抗共模干扰能力极强，支持<strong>多点通信</strong>和<strong>长距离</strong>（可达1200米），常用于工业环境。</td></tr></tbody></table><h3 id="2-IIC协议"><a href="#2-IIC协议" class="headerlink" title="2. IIC协议"></a>2. IIC协议</h3><h4 id="1）协议介绍-IIC"><a href="#1）协议介绍-IIC" class="headerlink" title="1）协议介绍-IIC"></a>1）协议介绍-IIC</h4><p>I2C（IIC）是一种两线式串行总线，采用“一主多从”的总线结构，每个设备都有唯一的设备地址，用于区分同一总线上的其他设备。</p><p>IIC总线由两根双向线组成：</p><ul><li><strong>SCL（串行时钟线）</strong>：用于同步数据传输。</li><li><strong>SDA（串行数据线）</strong>：用于发送和接收数据。</li></ul><p>通信由主设备发起，从设备被动响应。IIC具有低功耗和完善的应答机制，增强了通信的可靠性。</p><hr><h4 id="2）IIC的5种传输速率"><a href="#2）IIC的5种传输速率" class="headerlink" title="2）IIC的5种传输速率"></a>2）IIC的5种传输速率</h4><p>I2C协议支持以下5种速率模式，不同设备可能支持不同的速率：</p><ul><li><strong>标准模式（Standard）</strong>：100 kbps</li><li><strong>快速模式（Fast）</strong>：400 kbps</li><li><strong>快速模式+（Fast-Plus）</strong>：1 Mbps</li><li><strong>高速模式（High-speed）</strong>：3.4 Mbps</li><li><strong>超快模式（Ultra-Fast）</strong>：5 Mbps（单向传输）</li></ul><blockquote><p><strong>说明</strong>：超快模式通常用于LED、LCD等不需要应答的器件，仅进行写数据操作，不需要ACK应答信号。</p></blockquote><hr><h4 id="3）IIC的4种信号"><a href="#3）IIC的4种信号" class="headerlink" title="3）IIC的4种信号"></a>3）IIC的4种信号</h4><p>IIC协议的基础信号包括：起始信号、停止信号、应答信号和非应答信号。</p><ul><li><p><strong>起始信号</strong><br>当SCL为高电平时，SDA从高电平跳变到低电平，定义为起始信号（START）。</p></li><li><p><strong>停止信号</strong><br>当SCL为高电平时，SDA从低电平跳变到高电平，定义为停止信号（STOP）。</p></li><li><p><strong>应答信号</strong><br>从设备接收到主设备的数据后，在第9个SCL时钟周期内，将SDA拉低，表示“已收到数据”。</p></li><li><p><strong>非应答信号</strong><br>在第9个SCL时钟周期内，SDA保持高电平，表示“未收到数据”。</p></li></ul><hr><h4 id="4）数据有效性"><a href="#4）数据有效性" class="headerlink" title="4）数据有效性"></a>4）数据有效性</h4><p>IIC协议规定：</p><ul><li>数据采样发生在SCL高电平期间。</li><li>除了起始和停止信号外，数据传输期间，SCL为高电平时，SDA必须保持稳定；只有在SCL为低电平时，SDA才允许变化。</li></ul><hr><h4 id="5）读写时序"><a href="#5）读写时序" class="headerlink" title="5）读写时序"></a>5）读写时序</h4><ul><li><p><strong>写操作时序</strong><br>主设备向从设备的指定寄存器地址写入数据的时序如下：</p><ol><li><strong>主设备发送起始信号</strong>：通知总线上的所有设备开始通信。</li><li><strong>主设备发送从设备地址（含读/写位）</strong>：指定目标从设备，并指明接下来的操作是写入（写位为0）。</li><li><strong>从设备发送应答信号</strong>：从设备检测到自己的地址后，拉低SDA线，表示已准备好接收数据。</li><li><strong>主设备发送寄存器地址</strong>：指定从设备中目标寄存器的地址。</li><li><strong>从设备发送应答信号</strong>：确认接收到寄存器地址。</li><li><strong>主设备发送数据</strong>：将需要写入的数据发送到从设备。</li><li><strong>从设备发送应答信号</strong>：确认接收到数据。</li><li><strong>主设备发送停止信号</strong>：结束通信。</li></ol><blockquote><p><strong>说明</strong>：写操作时序中，主设备始终控制数据的发送，从设备仅负责接收数据并发送应答信号。</p></blockquote></li></ul><hr><ul><li><p><strong>读操作时序</strong><br>主设备从从设备的指定寄存器地址读取数据的时序如下：</p><ol><li><strong>主设备发送起始信号</strong>：通知总线上的所有设备开始通信。</li><li><strong>主设备发送从设备地址（含写位）</strong>：指定目标从设备，并指明接下来的操作是写入（写位为0）。</li><li><strong>从设备发送应答信号</strong>：从设备检测到自己的地址后，拉低SDA线，表示已准备好接收数据。</li><li><strong>主设备发送寄存器地址</strong>：指定从设备中目标寄存器的地址。</li><li><strong>从设备发送应答信号</strong>：确认接收到寄存器地址。</li><li><strong>主设备发送重复起始信号</strong>：重新发起通信，准备读取数据。</li><li><strong>主设备发送从设备地址（含读位）</strong>：指定目标从设备，并指明接下来的操作是读取（读位为1）。</li><li><strong>从设备发送应答信号</strong>：确认接收到读请求。</li><li><strong>从设备发送数据</strong>：将目标寄存器中的数据发送到主设备。</li><li><strong>主设备发送非应答信号</strong>：表示数据已接收完毕，不再需要更多数据。</li><li><strong>主设备发送停止信号</strong>：结束通信。</li></ol><blockquote><p><strong>说明</strong>：读操作时序中，主设备在发送寄存器地址后需要重新发起起始信号（重复起始信号），以切换到读取模式。</p></blockquote></li></ul><hr><h4 id="6）为什么IIC需要上拉电阻？"><a href="#6）为什么IIC需要上拉电阻？" class="headerlink" title="6）为什么IIC需要上拉电阻？"></a>6）为什么IIC需要上拉电阻？</h4><p>IIC总线的SCL和SDA线均为开漏（OD）输出，开漏输出只能输出低电平，不能输出高电平。为了使总线空闲时保持高电平，需要通过上拉电阻将其拉高。</p><p><strong>作用</strong>：</p><ul><li>确保总线空闲时为高电平。</li><li>提高信号的抗干扰能力。</li></ul><hr><h3 id="3-SPI协议"><a href="#3-SPI协议" class="headerlink" title="3. SPI协议"></a>3. SPI协议</h3><h4 id="1）协议介绍-SPI"><a href="#1）协议介绍-SPI" class="headerlink" title="1）协议介绍-SPI"></a>1）协议介绍-SPI</h4><p>SPI（Serial Peripheral Interface）是一种高速、全双工、同步的通信总线，通常用于主设备（Master）与从设备（Slave）之间的通信。SPI仅需4根线即可实现通信，分别是：</p><ul><li><strong>MISO</strong>：主设备输入，从设备输出。</li><li><strong>MOSI</strong>：主设备输出，从设备输入。</li><li><strong>SCLK</strong>：串行时钟信号，由主设备生成。</li><li><strong>CS</strong>：片选信号，用于选择具体的从设备（低电平有效）。</li></ul><hr><h4 id="2）通信原理"><a href="#2）通信原理" class="headerlink" title="2）通信原理"></a>2）通信原理</h4><ul><li>主设备通过拉低片选信号（CS）选择目标从设备。</li><li>数据通过MOSI和MISO线传输，时钟信号（SCLK）同步数据采样。</li><li>数据采样时机由时钟的极性（CPOL）和相位（CPHA）决定，这是SPI的两个主要参数，<strong>而IIC固定，由SCL高电平时采样</strong></li></ul><hr><h4 id="3）优缺点"><a href="#3）优缺点" class="headerlink" title="3）优缺点"></a>3）优缺点</h4><p><strong>优点</strong>：</p><ul><li><strong>高速全双工通信</strong>。</li><li>硬件简单，支持任意数据长度。</li><li><strong>支持一主多从</strong></li></ul><p><strong>缺点</strong>：</p><ul><li>需要更多引脚（4根线）。</li><li><strong>无硬件应答信号</strong>，缺乏错误检测机制。</li><li>通信距离较短，通常仅支持一个主设备。</li></ul><hr><h3 id="4-CAN协议"><a href="#4-CAN协议" class="headerlink" title="4. CAN协议"></a>4. CAN协议</h3><h4 id="1）协议介绍"><a href="#1）协议介绍" class="headerlink" title="1）协议介绍"></a>1）协议介绍</h4><p>CAN（Controller Area Network）是一种多主方式的串行通信总线，最初由德国 Bosch 公司在 1980 年代末提出，广泛应用于汽车电子、工业自动化等领域。CAN总线具有高实时性、可靠性和抗干扰能力，支持多节点通信。</p><h4 id="2）CAN总线结构"><a href="#2）CAN总线结构" class="headerlink" title="2）CAN总线结构"></a>2）CAN总线结构</h4><ul><li><strong>闭环结构</strong>：两根信号线（CANH 和 CANL）形成回路，总线两端连接 120 欧姆终端电阻，支持<strong>高速短距离</strong>通信（125 kbps ~ 1 Mbps，最长 40 米）。</li><li><strong>开环结构</strong>：两根信号线独立，各自串联 2.2 kΩ 电阻，支持<strong>低速长距离</strong>通信（最高 125 kbps，最长 1000 米）。</li><li><strong>差分信号传输</strong>：通过 CANH 和 CANL 的电压差传递信息，具有<strong>良好的抗干扰能力</strong>。</li></ul><h4 id="3）CAN总线特点"><a href="#3）CAN总线特点" class="headerlink" title="3）CAN总线特点"></a>3）CAN总线特点</h4><ul><li><strong>实时性</strong>：仲裁机制和帧优先级设计保证低延迟和高实时性。</li><li><strong>多主机系统</strong>：支持多个节点同时发送和接收数据，无主从之分。</li><li><strong>差分信号传输</strong>：抗干扰能力强，适用于工业环境。</li><li><strong>仲裁机制</strong>：通过消息标识符优先级决定发送权，避免冲突。</li><li><strong>广播通信</strong>：数据帧可被所有节点接收，便于信息共享。</li><li><strong>错误检测</strong>：内置 CRC 校验和错误处理机制，确保通信可靠性。</li><li><strong>灵活性</strong>：支持不同波特率，适应多种应用场景。</li></ul><h4 id="4）CAN总线帧类型"><a href="#4）CAN总线帧类型" class="headerlink" title="4）CAN总线帧类型"></a>4）CAN总线帧类型</h4><table><thead><tr><th align="left">帧类型</th><th align="left">帧用途</th></tr></thead><tbody><tr><td align="left"><strong>数据帧</strong> (Data frame)</td><td align="left">节点发送的包含ID和数据的帧，用于发送单元向接收单元传送数据。</td></tr><tr><td align="left"><strong>遥控帧</strong> (Remote frame)</td><td align="left">接收单元向发送单元请求发送具有特定标识符的数据所用的帧，本身不包含数据段。</td></tr><tr><td align="left"><strong>错误帧</strong> (Error frame)</td><td align="left">当节点检测出错误时，用于向其他所有单元通知错误的帧。</td></tr><tr><td align="left"><strong>过载帧</strong> (Overload frame)</td><td align="left">接收单元通知其尚未做好接收准备的帧，用于请求发送节点延迟发送。</td></tr><tr><td align="left"><strong>帧间隔</strong> (Inter-frame space)</td><td align="left">用于将数据帧及遥控帧与前面的帧分离开来的段，包含间歇和总线空闲段。</td></tr></tbody></table><h4 id="5）优缺点"><a href="#5）优缺点" class="headerlink" title="5）优缺点"></a>5）优缺点</h4><p><strong>优点</strong>：</p><ul><li>高实时性和可靠性。</li><li>支持多主机通信,当多主机同时发送消息时，有优先级法则来控制先后发送</li><li>抗干扰能力强，适合工业环境。</li></ul><p><strong>缺点</strong>：</p><ul><li>通信速率受限于总线长度。</li><li>半双工通信，不能同时发送和接收。</li><li>无时钟信号，需统一波特率。</li></ul><hr><p>CAN协议还有优先级法则，可以参考<a class="link" href="https://blog.csdn.net/qq_35057766/article/details/135580884">一文读懂CAN总线协议<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><hr><h4 id="6）系统学习内容-B站江科大CAN总线"><a href="#6）系统学习内容-B站江科大CAN总线" class="headerlink" title="6）系统学习内容(B站江科大CAN总线)"></a>6）系统学习内容(B站江科大CAN总线)</h4><h5 id="6-1-整体概括"><a href="#6-1-整体概括" class="headerlink" title="6.1 整体概括"></a>6.1 整体概括</h5><ol><li><p>CAN总线是一种 <strong>局域网总线，简介易用，速度快，可靠的串行通信总线</strong></p></li><li><p><strong>CAN_H</strong>  <strong>CAN_L</strong>  (<strong>差分信号</strong>)，原则上可以不共地使用，但可能会让收发器承受较高的共模电压，所以<strong>一般还是共地</strong>，差分的好处就是<strong>抗干扰能力强</strong></p></li><li><p>高速CAN(<strong>闭环，速度快，距离近</strong>)  &amp;  低速CAN(<strong>开环，速度慢，距离远</strong>) 高速的更常用</p></li><li><p><strong>异步通信，不需用时钟线，依靠波特率</strong></p></li><li><p><strong>半双工，可以挂载多个设备</strong>，如果多设备同时发送信号，<strong>通过仲裁判断先后发送数据</strong></p></li><li><p><strong>报文ID</strong>(11位/29位),用来<strong>区分消息功能</strong>，同时<strong>确定优先级</strong></p></li><li><p><strong>一个数据帧，可以配置1~8个字节的有效载荷</strong>(而串口只能一次一个字节)</p></li><li><p>发送模式: <strong>广播式</strong> 或者 <strong>请求式</strong></p></li><li><p>应答，CRC校验，位填充，位同步，错误处理等特性</p></li></ol><hr><h5 id="6-2-硬件电路"><a href="#6-2-硬件电路" class="headerlink" title="6.2 硬件电路"></a>6.2 硬件电路</h5><p>闭环的高速CAN 通过<strong>CAN_L 和 CAN_H 连接两个终端电阻</strong>(不是很大)，来使得<strong>默认呈现1</strong>，当<strong>输出0的时候通过CAN收发器来实现</strong></p><p>高速CAN规定: 电<strong>压差为0V的时候表示逻辑1(隐形电平，两线合并)，电压差为2V的时候表示为逻辑0(显性电平，两线张开)</strong></p><p>低速CAN：电压差为-1.5表示逻辑1，电压差为3V表示0</p><hr><h5 id="6-3-帧格式"><a href="#6-3-帧格式" class="headerlink" title="6.3 帧格式"></a>6.3 帧格式</h5><ul><li><strong>数据帧</strong> RTR为显性电平0</li></ul><p><strong>帧起始</strong> + <strong>仲裁帧</strong>(id/优先级) + <strong>控制段</strong>(选择标准格式/拓展格式+保留位+数据段的长度) + <strong>数据段</strong> + <strong>CRC段</strong> + <strong>ACK段</strong> + <strong>帧结束</strong></p><hr><ul><li><strong>遥控帧</strong> RTR为隐性电平1</li></ul><p>和数据帧的区别是<strong>没有数据段</strong></p><hr><ul><li><strong>错误帧</strong></li></ul><p>当<strong>任意设备发现总线上的数据发生错误，比如位填充错误，CRC校验错误等，就会发出错误帧来破坏数据</strong></p><hr><ul><li><strong>过载帧</strong></li></ul><p>当设备<strong>在接受大量数据处理不过来的时候，可以发送过载帧来延缓发送</strong></p><hr><ul><li><strong>帧间隔</strong></li></ul><p>在前面各种格式的帧之间的间隔</p><hr><ul><li><strong>位填充</strong></li></ul><p>当数据连续发送<strong>五个一样的数据</strong>的时候，发送的时候应该在末尾<strong>填充一个相反的数据</strong><br>目的是为了<strong>防止长时间保持同一个信号</strong>，防止被认为总线空闲，同时可以区分数据帧和错误帧/帧间隔等<br>比如发送 <code>11100000110000</code><br>那么实际应该发送: <code>11100000**1**110000</code> 需要补充一个与五个0相反的1发送<br>同时接收方也需要根据这个规则来还原原始数据</p><hr><h5 id="6-4-帧格式"><a href="#6-4-帧格式" class="headerlink" title="6.4 帧格式"></a>6.4 帧格式</h5><hr><h3 id="4-RS-485-RS-232-USB"><a href="#4-RS-485-RS-232-USB" class="headerlink" title="4. RS-485 & RS-232 & USB"></a>4. RS-485 &amp; RS-232 &amp; USB</h3><blockquote><p>RS-232、RS-485 和 USB 对比</p></blockquote><table><thead><tr><th align="left">特性</th><th align="left"><strong>RS-232</strong></th><th align="left"><strong>RS-485</strong></th><th align="left"><strong>USB (Universal Serial Bus)</strong></th></tr></thead><tbody><tr><td align="left"><strong>通信方式</strong></td><td align="left"><strong>点对点</strong> (Peer-to-Peer)</td><td align="left"><strong>多点</strong> (Multi-drop)，支持总线式拓扑，一台主设备可带多个从设备</td><td align="left"><strong>主从结构</strong> (Host-Slave) 的<strong>树形拓扑</strong>，一个主机最多可连接127个设备（需通过集线器扩展）</td></tr><tr><td align="left"><strong>信号类型</strong></td><td align="left"><strong>单端信号</strong> (Single-ended)</td><td align="left"><strong>差分信号</strong> (Differential)</td><td align="left"><strong>差分信号</strong> (Differential) (D+ 和 D-)</td></tr><tr><td align="left"><strong>传输距离</strong></td><td align="left">短（通常 ≤ <strong>15米</strong>）</td><td align="left">长（可达 <strong>1200米</strong>，速率降低时更远）</td><td align="left">短（通常 <strong>≤ 5米</strong>，高速信号衰减限制；可通过有源延长器扩展）</td></tr><tr><td align="left"><strong>最大速率</strong></td><td align="left">较低（通常 <strong>115.2 kbps</strong>，特定条件下可达更高）</td><td align="left">较高（<strong>10 Mbps 短距离</strong>，距离增加则速率下降）</td><td align="left">极高（<strong>USB 2.0: 480 Mbps</strong>; <strong>USB 3.0: 5 Gbps</strong>; USB 4: 40 Gbps）</td></tr><tr><td align="left"><strong>抗干扰能力</strong></td><td align="left">弱（单端信号易受共模噪声影响）</td><td align="left"><strong>强</strong>（差分信号天生抗共模干扰）</td><td align="left"><strong>强</strong>（差分信号抗干扰；协议层有CRC校验和重传机制）</td></tr><tr><td align="left"><strong>供电能力</strong></td><td align="left">无（仅定义信号，不提供电源）</td><td align="left">无（仅定义信号，不提供电源）</td><td align="left"><strong>有</strong>（提供 <strong>5V</strong> 电源，USB 2.0最大500mA，USB 3.0最大900mA，USB PD协议可支持更高功率）</td></tr><tr><td align="left"><strong>典型应用</strong></td><td align="left">工业控制、老式调制解调器、串口调试</td><td align="left">工业自动化、楼宇自控、长距离数据采集</td><td align="left">外设连接（键盘、鼠标、打印机）、大容量存储（U盘、移动硬盘）、高速数据采集</td></tr><tr><td align="left"><strong>成本与复杂度</strong></td><td align="left"><strong>低</strong>（硬件简单，协议简单）</td><td align="left"><strong>中</strong>（需终端电阻，协议简单）</td><td align="left"><strong>高</strong>（协议复杂，需专用控制器和驱动）</td></tr></tbody></table><hr><h3 id="5-TCP-UDP-HTTP"><a href="#5-TCP-UDP-HTTP" class="headerlink" title="5. TCP & UDP & HTTP"></a>5. TCP &amp; UDP &amp; HTTP</h3><table><thead><tr><th><strong>对比维度</strong></th><th><strong>TCP（传输控制协议）</strong></th><th><strong>UDP（用户数据报协议）</strong></th></tr></thead><tbody><tr><td><strong>连接方式</strong></td><td>面向连接（<strong>三次握手，四次挥手</strong>）</td><td>无连接（<strong>直接发送数据</strong>，无需建立或释放连接）</td></tr><tr><td><strong>可靠性</strong></td><td><strong>可靠</strong>（保证数据不丢失、不重复、按序到达）</td><td>不可靠（可能丢包、乱序，需应用层处理）</td></tr><tr><td><strong>传输模式</strong></td><td>字节流（将数据视为连续的字节序列）</td><td><strong>数据报</strong>（以独立数据包为单位传输，边界清晰）</td></tr><tr><td><strong>拥塞控制</strong></td><td>支持（通过慢启动、拥塞避免等算法调节发送速率）</td><td>不支持（无论网络状态如何，持续按原速率发送）</td></tr><tr><td><strong>流量控制</strong></td><td>支持（滑动窗口机制，避免接收方缓冲区溢出）</td><td>不支持（可能导致接收方缓冲区溢出）</td></tr><tr><td><strong>头部开销</strong></td><td>较大（固定 20 字节头部，可选扩展字段）</td><td>极小（固定 8 字节头部，无扩展字段）</td></tr><tr><td><strong>传输效率</strong></td><td>较低（连接建立、确认重传等过程增加延迟）</td><td>较高（无连接建立和确认机制，实时性强）</td></tr><tr><td><strong>适用场景</strong></td><td>文件传输、网页加载、登录认证等对<strong>可靠性要求高的场景</strong></td><td>视频直播、在线游戏、物联网等对<strong>实时性要求高的场景</strong></td></tr><tr><td><strong>端口占用</strong></td><td>单端口可建立多个连接（基于四元组：源 IP/端口、目的 IP/端口）</td><td>单端口对应一个连接（数据报直接绑定端口）</td></tr><tr><td><strong>错误处理</strong></td><td>自动重传（超时重传、快速重传机制）</td><td>不处理（丢包需应用层自行解决）</td></tr></tbody></table><p>二者最大的区别就是：<strong>TCP可靠，UDP实时</strong></p><hr><p><strong>TCP（传输控制协议）和 HTTP（超文本传输协议）</strong>，二者分别位<strong>于传输层</strong>，<strong>应用层</strong>，<strong>HTTP协议依赖于TCP协议</strong>，(值得注意的是，传统的HTTP（/1.1和/2）基于TCP，但也存在一些局限性，如队头阻塞。新一代的HTTP/3做出了重大改变，它不再使用TCP作为传输层协议，而是基于QUIC协议（运行在UDP之上）​，旨在进一步提升传输效率和降低延迟)</p><hr><blockquote></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#51;&#49;&#52;&#54;&#55;&#x30;&#x32;&#51;&#54;&#50;&#64;&#x71;&#x71;&#x2e;&#99;&#x6f;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">总结常用的硬件通信协议(UART,SPI、IIC、CAN、USB等)</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
  </entry>
  
  <entry>
    <title>RT-Thread学习笔记</title>
    <link href="https://blog.haozi-haozi.cn/2025/03/15/embedded_rt_thread/"/>
    <id>https://blog.haozi-haozi.cn/2025/03/15/embedded_rt_thread/</id>
    <published>2025-03-15T03:22:45.000Z</published>
    <updated>2026-03-24T08:58:32.941Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-概念相关"><a href="#A-概念相关" class="headerlink" title="A 概念相关"></a>A 概念相关</h2><h3 id="1-RT-Thread中提供的线程调度器是基于优先级的全抢占式调度"><a href="#1-RT-Thread中提供的线程调度器是基于优先级的全抢占式调度" class="headerlink" title="1. RT-Thread中提供的线程调度器是基于优先级的全抢占式调度"></a>1. RT-Thread中提供的线程调度器是基于优先级的全抢占式调度</h3><blockquote><p>全抢占式调度</p></blockquote><p>线程调度器会通过<strong>线程就绪表</strong>来查找当前就绪态任务中 最高优先级 的任务来执行</p><p>在系统中除了</p><ol><li>中断处理函数(硬件中断等)</li><li>调度器上锁部分的代码(暂时停止线程调度，但依旧会被硬件中断等响应)</li><li>禁止中断的代码(​不被任何可屏蔽中断和线程抢占)是不可被强占的，系统的其他部分都是可以抢占的，包括线程调度器自身。</li></ol><p>其中，禁止中断的实现原理是 rt_hw_interrupt_disable()和 rt_hw_interrupt_enable()函数来实现的，本质上是执行特定的CPU指令，但也只能关闭可屏蔽中断</p><p>不可屏蔽中断：这种中断通常用于处理极其严重的硬件错误（如内存访问错误、看门狗超时等），其优先级最高，​无法通过软件禁止。因此，即使在临界区内，如果发生NMI，CPU也会立即暂停当前代码的执行，转去执行NMI的中断服务程序。待NMI处理完毕后，再返回临界区继续执行。</p><blockquote><p>时间片轮转(分时调度器)</p></blockquote><p>RT-Thread内核中也允许创建相同优先级的线程。<strong>相同优先级的线程采用时间片轮转方式进行调度</strong>（也就是通常说的分时调度器），时间片轮转调度仅在当前系统中无更高优先级就绪线程存在的情况下才有效。</p><h3 id="2-对于运行RT-Thread操作系统，线程都处于五种状态之一"><a href="#2-对于运行RT-Thread操作系统，线程都处于五种状态之一" class="headerlink" title="2. 对于运行RT-Thread操作系统，线程都处于五种状态之一"></a>2. 对于运行RT-Thread操作系统，线程都处于五种状态之一</h3><blockquote><p>初始状态、就绪状态、运行状态、挂起状态、关闭状态</p></blockquote><p>通过调用操作系统提供的接口函数，可以让线程在这五种状态中进行来回切换</p><pre class="mermaid">graph TD    A[初始状态<br>RT_THREAD_INIT] --&gt;|rt_thread_startup| B(就绪状态<br>RT_THREAD_READY)    B --&gt;|调度器调度| C{运行状态<br>RT_THREAD_RUNNING}    C --&gt;|主动延时: rt_thread_delay/<br>等待资源: rt_sem_take, rt_mb_recv等<br>或资源不可用| D[挂起状态<br>RT_THREAD_SUSPEND]    D --&gt;|等待超时或资源可用| B    C --&gt;|线程执行完毕| E[[关闭状态<br>RT_THREAD_CLOSE]]    D --&gt;|rt_thread_delete/detach| E    C --&gt;|时间片用完或更高优先级线程就绪| B    style A fill:#e6f7ff    style B fill:#e6ffe6    style C fill:#fff0e6    style D fill:#ffe6e6    style E fill:#f9f9f9</pre><blockquote><p>空闲线程</p></blockquote><p>空闲线程是系统线程中一个比较特殊的线程，它具有最低的优先级，当系统中无其他线程可运行时，调度器将调度到空闲线程。空闲线程通常是一个死循环，永远不被挂起</p><p>其中，空闲线程还有一个重要作用:<br>因为RT-Thread 的线程删除并非一步到位，而是分为<strong>设置关闭状态</strong>和<strong>空闲线程回收</strong>​两个阶段</p><ol><li><p>当我们在代码中调用 rt_thread_delete()或 rt_thread_detach()时，内核并不会立即释放该线程的资源（如控制块和栈空间）。它只是将该线程的状态标记为 ​RT_THREAD_CLOSE，并将其从就绪队列等调度列表中移除，同时挂入一个名为 rt_thread_defunct的队列（常被称为“僵尸线程队列”）至此，该线程不再参与系统调度。</p></li><li><p><strong>真正的资源释放工作由空闲线程完成</strong>,包括释放线程控制块和线程栈所占用的内存</p></li></ol><h3 id="3-RT-Thread的程序内存分布"><a href="#3-RT-Thread的程序内存分布" class="headerlink" title="3. RT-Thread的程序内存分布"></a>3. RT-Thread的程序内存分布</h3><blockquote><p>​Flash（ROM） 和 RAM 是两种不同的物理介质</p></blockquote><p>1.​Flash (ROM)​​：相当于“仓库”或“硬盘”。特点是非易失性​（断电后数据<strong>不丢失</strong>），但读写速度较慢。用于永久存储程序代码、常量数据等。</p><p>​2. RAM​：相当于“工作台”或“内存条”。特点是易失性​（断电后数据<strong>丢失</strong>），但读写速度极快。用于程序运行时存放临时变量、函数栈、以及需要被更改的数据。</p><p>程序要运行，CPU 必须从 ​RAM​ 中取指令、读/写数据。而上电时，所有程序和数据都安静地躺在 ​Flash​ 里。因此，需要一个机制将必要的内容从“仓库”（Flash）搬运到“工作台”（RAM）上，这个工作就是由启动文件中的汇编代码完成的。</p><p>编译后，程序被分成了几个重要的“段（Section）”，三个最关键的段：<strong>RO，RW，ZI</strong>。</p><ul><li><strong>RO段 (ReadOnly)</strong> - 只读段</li></ul><p>主要存放 Code(代码) 和 RO Data(只读数据)，比如const修饰的全局变量和静态变量，字符串常量等等</p><ul><li><strong>RW段 (ReadWrite)</strong> - 可读写数据段</li></ul><p>主要存放 所有已初始化且初值不为 0​ 的全局变量和静态变量等等</p><ul><li><strong>ZI段 (ZeroInitialized)</strong> - 零初始化数据段</li></ul><p>主要存放 所有未初始化的全局变量/静态变量，以及所有显式初始化为 0​ 的全局变量/静态变量。<br>上电后，启动代码会将在 RAM 中为 ZI 段分配的整个区域<strong>全部清零</strong>，所以未初始化的变量默认为0</p><ul><li><strong>动态内存堆</strong> <strong>主栈空间​</strong>- 剩余的RAM区域</li></ul><p>在RT-Thread中，栈主要分为 <strong>主栈</strong>(存储main函数的局部变量、函数调用返回地址、参数传递、以及中断服务例程（ISR）的上下文)，<strong>线程栈</strong>(创建线程的时候分配)，<strong>中断栈</strong>(通常会使用调用中断的线程的栈，有时候为了防止线程栈不足，也可以配置一个独立的中断栈))</p><blockquote><p>上电启动流程图</p></blockquote><pre class="mermaid">flowchart TD    A[上电复位] --&gt; B[CPU从Flash固定地址<br>取复位向量执行]    B --&gt; C[初始化必要硬件<br>设置堆栈指针/系统时钟]    C --&gt; D[复制RW段数据<br>从Flash至RAM]    D --&gt; E[清零ZI段<br>对应RAM区域初始化]    E --&gt; F{C语言运行环境<br>准备就绪}    F --&gt;|是| G[跳转至main函数]    G --&gt; H[执行用户程序]        style A fill:#7e9,stroke:#333,stroke-width:2px    style H fill:#7e9,stroke:#333,stroke-width:2px    style C fill:#69f,stroke:#333,stroke-width:2px    style D fill:#69f,stroke:#333,stroke-width:2px    style E fill:#69f,stroke:#333,stroke-width:2px    style F fill:#fc3,stroke:#333,stroke-width:2px</pre><blockquote><p>SRAM、DRAM、SDRAM的区别</p></blockquote><p>SRAM：静态的随机存储器，加电情况下，<strong>不需要刷新，数据不会丢失</strong>，CPU的缓存就是SRAM<br>DRAM：动态随机存储器，加电情况下，也<strong>需要不断刷新，才能保存数据</strong>，最为常见的系统内存<br>SDRAM：同步动态随机存储器，即数据的读取<strong>需要时钟来同步，也可用作内存</strong></p><blockquote><p>两种创建进程的区别(静态/动态)</p></blockquote><ul><li>创建线程(静态)—–<strong>占用RAM空间(RW/ZI 空间)，用户分配栈空间和线程句柄</strong>,位置由编译的时候确定</li></ul><p>优点: 运行的时候不用动态分配内存，<strong>运行效率高，实时性好</strong><br>缺点: 内存不能被释放，之内使用 rt_thread_detach() 函数该线程脱离管理</p><ul><li>创建线程(动态)—–<strong>由RT-Thread内核自动从内存堆中为线程控制块和线程栈分配所需的内存</strong></li></ul><p>优点: 灵活性高，可以随时根据系统状态和任务需求来创建或删除线程，<strong>更好的利用内存资源</strong><br>缺点: 动态分配内存需要时间，实时性降低; 并且频繁创建删除线程可能产生内存碎片，影响后续的内存分配</p><h3 id="4-定时器管理"><a href="#4-定时器管理" class="headerlink" title="4. 定时器管理"></a>4. 定时器管理</h3><blockquote><p>硬件定时器和软件定时器</p></blockquote><ul><li><p>硬件定时器<br>可以理解成裸机stm32使用的定时器外设，一般是由外部晶振提供给芯片输入时钟，芯片向软件模块提供一组配置寄存器，接受控制输入，到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的<strong>精度一般很高</strong>，可以达到纳秒级别，并且是<strong>中断触发方式</strong></p></li><li><p>软件定时器<br>软件定时器是由操作系统提供的一类系统接口（函数），它<strong>构建在硬件定时器基础之上</strong>，使系统能够提供<strong>不受数目限制</strong>的定时器服务，但<strong>精度越高，对系统开销也将越大</strong>(在1秒中系统用于处理时钟中断的次数也就越多）)</p></li></ul><p>在操作系统中，通常软件定时器以系统节拍（tick）为单位。节拍长度指的是周期性硬件定时器两次中断间的间隔时间长度。这个周期性硬件定时器也称之为操作系统时钟<br>软件定时器以这个节拍时间长度为单位，数值必须是这个节拍的<strong>整数倍</strong></p><h3 id="5-任务间同步及通信"><a href="#5-任务间同步及通信" class="headerlink" title="5. 任务间同步及通信"></a>5. 任务间同步及通信</h3><blockquote><p>处理临界区</p></blockquote><p>为了防止多个线程在同一时间访问同一个地址内存，这将引起数据一致性的问题，所以引入信号量等多个方法<br>对于<strong>操作/访问同一块区域，称之为临界区</strong>。任务的同步方式有很多种，其核心思想都是：在访问临界区的时候只允许一个(或一类)任务运行。</p><p>当然，也可以<strong>原子操作</strong>，防止多个线程通常修改某个变量</p><hr><ul><li><strong>中断锁(关闭中断)</strong></li></ul><p>这便是最开始说的关闭中断的代码提到的关闭中断，硬件中断也不会打断当前任务进行<br>当中断关闭的时候，就意味着<strong>当前任务不会被其他事件打断</strong>（因为整个系统已经不再响应那些可以触发线程重新调度的外部事件），也就是当前线程不会被抢占，除非这个任务主动放弃了处理器控制权</p><p>优点：使用得当的时候一种<strong>快速、高效</strong>的同步方式<br>缺点：中断锁对系统的<strong>实时性影响非常巨大</strong>，当使用不当的时候会导致系统完全无实时性可言，通常只会在中断锁里面执行几句机器指令</p><hr><ul><li><strong>调度器锁</strong></li></ul><p>对调度器上锁，系统依然能响应外部中断，<strong>中断服务例程依然能进行相应的响应</strong><br>也就是使得当前线程不会被其它线程抢占，但依旧会被硬件中断等抢占，所以要考虑硬件中断中是否会修改临界资源</p><hr><ul><li><strong>条件变量</strong></li></ul><p>条件变量其实就是一个信号量，用于线程间同步。条件变量用来阻塞一个线程，<strong>当条件满足时向阻塞的线程发送一个条件，阻塞线程就被唤醒</strong>，条件变量需要和互斥锁配合使用，互斥锁用来保护共享数据。</p><hr><ul><li><strong>读写锁</strong></li></ul><p>读写锁也称为多读者单写者锁。<strong>读写锁把对共享资源的访问者划分成读者和写者</strong>，读者只对共享资源进行读访问，写者则需要对共享资源进行写操作。同一时间只能有一个线程可以占有写模式的读写锁,但是可以有多个线程同时占有读模式的读写锁。读写锁适合于对数据结构的读次数比写次数多得多的情况，因为读模式锁定时可以共享,写模式锁定时意味着独占。</p><p>读写锁通常是基于互斥锁和条件变量实现的。一个线程可以对一个读写锁进行多次读写锁定，同样必须有对应次数的解锁。</p><hr><ul><li><strong>信号量</strong></li></ul><p>线程通过获取信号量来获得信号量资源实例，当信号量值大于零时，线程将获得信号量，并且<strong>相应的信号量值都会减1</strong><br>若信号量值等于零的时候，那么说明当前信号量资源实例不可用，申请该信号量的线程将根据time参数的情况选择<strong>直接返回、或挂起等待一段时间、或永久等待</strong>，直到其他线程或中断释放该信号量。<br>如果在参数time指定的时间内依然得不到信号量，线程将超时返回，返回值是-RT_ETIMEOUT<br><strong>当线程完成资源的访问后，应该释放它持有的信号量</strong></p><p>信号量是一种非常灵活的同步方式，可以运用在多种场合中<br><strong>形成锁，中断与线程等之间的同步，资源计数等关系，也能方便的用于线程与线程，中断与线程的同步中。</strong></p><hr><ul><li><strong>互斥量</strong></li></ul><p>互斥量是一种特殊的二值性信号量，它和信号量不同的是，它<strong>支持互斥量所有权、递归访问以及防止优先级翻转的特性</strong>。</p><p>互斥量所有权，即谁拥有，只能谁释放，会降低出现死锁的概率</p><hr><ul><li><strong>优先级翻转:</strong></li></ul><p>优先级翻转是实时系统中一种特殊现象，指高优先级任务因所需资源被低优先级任务占用而被阻塞，而低优先级任务又可能被中优先级任务抢占，<strong>导致高优先级任务长时间得不到执行，系统实时性遭到破坏，此时优先级顺序会变成中等优先级&gt;低优先级&gt;高优先级</strong></p><p>解决方法:</p><ol><li><p><strong>优先级继承协议​​</strong><br>当高优先级任务（H）被低优先级任务（L）阻塞时，<strong>​临时将L的优先级提升至H的优先级​</strong><br>优点：实现相对简单，<em>仅在发生阻塞时动态提升，比较精准</em><br>缺点：仍需处理可能的阻塞链，若嵌套复杂需多次判断和提升优先级</p></li><li><p><strong>​优先级天花板​</strong><br>为每个资源<strong>预设一个​天花板优先级</strong>​​（等于可能访问该资源的最高任务优先级）<br>优点：能完全避免优先级翻转，且同时可预防死锁<br>缺点：需预先静态设置天花板优先级，可能不够灵活，有时会过度提升优先级</p></li></ol><hr><ul><li><strong>事件</strong></li></ul><p>事件主要用于线程间的同步，与信号量不同，它的特点是可以实现<strong>一对多，多对多</strong>的同步，即一个线程可等待多个事件的触发<br><strong>线程通过“逻辑与”或“逻辑或”与一个或多个事件建立关联</strong></p><hr><ul><li><strong>邮箱</strong></li></ul><p>邮箱服务是实时操作系统中一种典型的任务间通信方法，特点是<strong>开销比较低，效率较高</strong><br>邮箱中的每一封邮件只能容纳固定的4字节内容（针对32位处理系统，指针的大小即为4个字节，所以一封邮件恰好能够容纳一个指针）)<br><strong>不可以在中断中接收，或等待发送邮件</strong>，中断中阻塞会影响系统实时性</p><hr><ul><li><strong>消息队列</strong></li></ul><p>它能够接收来自线程或中断服务例程中<strong>不固定长度的消息</strong><br>消息队列是一种<strong>异步</strong>通信方式。<br><strong>不可以在中断中接收</strong></p><h3 id="6-虚拟文件系统"><a href="#6-虚拟文件系统" class="headerlink" title="6. 虚拟文件系统"></a>6. 虚拟文件系统</h3><blockquote><p>RT-Thread 的文件系统采用了三层的结构,对上层提供的接口主要以POSIX 标准接口为主</p></blockquote><pre class="mermaid">flowchart TD    A["应用程序调用POSIX接口<br>e.g. open, write"] --&gt; B["DFS 虚拟文件系统层<br>接收请求"]    B --&gt; C{"查找文件路径对应的<br>具体文件系统"}    C --&gt; D["内存文件系统<br>（如tmpfs, ramfs）"]    D --&gt; E["数据保存在<br>系统RAM内存中"]    E --&gt; F["特点: 速度快<br>断电丢失"]    C --&gt; G["存储设备文件系统<br>（如FatFS, LittleFS）"]    G --&gt; H["数据写入块设备<br>（如SPI Flash, SD卡）"]    H --&gt; I["特点: 持久化存储<br>读写速度相对较慢"]</pre><p>在使用文件系统接口前，<strong>需要对文件系统进行初始化</strong>,也就是挂载相关操作，这里定义挂载的文件系统及其类型和挂载点</p><p>在使用文件系统接口的时候，<strong>虚拟文件系统会根据你的路径，判断是保存在SD卡，或者是内存中</strong>，而我们只用<strong>通过统一的 POSIX 接口（如 open、write）操作文件</strong>，不需要关心底层如何实现</p><h2 id="B-设备和驱动"><a href="#B-设备和驱动" class="headerlink" title="B.设备和驱动"></a>B.设备和驱动</h2><h3 id="1-I-O设备模型"><a href="#1-I-O设备模型" class="headerlink" title="1. I/O设备模型"></a>1. I/O设备模型</h3><p>这里的操作和学习imx6ull中的驱动操作类似，这是这里我们主要操作应用态，而linux驱动主要在操作内核态</p><blockquote><p>在 Linux 驱动开发中，我们通常需要直接编写运行在内核态的字符设备/块设备驱动，涉及大量的内核 API（file_operations、register_chrdev等）。而在 RT-Thread 中，这套 I/O 设备模型为我们完成了绝大部分的“内核态”工作。</p></blockquote><pre class="mermaid">flowchart TD    A[应用程序]        subgraph B [I/O 设备管理层]        B1[I/O设备管理接口]        B2[字符设备]        B3[块设备]        B4[SPI总线设备]        B5[SPI从设备]        B6[I2C总线设备]        B7[其他设备类型...]    end    subgraph C [设备驱动框架层]        C1[串口设备驱动框架]        C2[SPI设备驱动框架]        C3[I2C设备驱动框架]        C4[PIN设备驱动框架]        C5[...]    end    subgraph D [设备驱动层]        D1[STM32/NXP 串口驱动]        D2[各类SPI控制器驱动]        D3[SPI Flash驱动]        D4[STM32/NXP I2C驱动]        D5[STM32/NXP GPIO驱动]        D6[...]    end    E[硬件]    A --&gt; B1    B1 --&gt; B2    B1 --&gt; B3    B1 --&gt; B4    B1 --&gt; B5    B1 --&gt; B6    B1 --&gt; B7    B2 --&gt; C1    B3 --&gt; C1    B4 --&gt; C2    B5 --&gt; C2    B6 --&gt; C3    B7 --&gt; C4 &amp; C5    C1 --&gt; D1    C2 --&gt; D2 &amp; D3    C3 --&gt; D4    C4 --&gt; D5    C5 --&gt; D6    D1 --&gt; E    D2 --&gt; E    D3 --&gt; E    D4 --&gt; E    D5 --&gt; E    D6 --&gt; E</pre><p>驱动层负责创建设备实例，并注册到 I/O 设备管理器中，可以<strong>通过静态申明的方式创建设备实例</strong>，也可以用下面的接口进行动态创建,<strong>不使用后也需要销毁</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">rt_device_t</span> <span class="title function_">rt_device_create</span><span class="params">(<span class="type">int</span> type, <span class="type">int</span> attach_size)</span>;  <span class="comment">//创建</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">rt_device_destroy</span><span class="params">(<span class="type">rt_device_t</span> device)</span>; <span class="comment">//销毁</span></span><br></pre></td></tr></table></figure></div><p>设备被创建后，<strong>需要注册到 I/O 设备管理器中</strong>，应用程序才能够访问，注册设备的函数如下所示，<strong>不使用之后也需要注销</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">rt_err_t</span> <span class="title function_">rt_device_register</span><span class="params">(<span class="type">rt_device_t</span> dev, <span class="type">const</span> <span class="type">char</span>* name, <span class="type">rt_uint8_t</span> flags)</span>; <span class="comment">//注册</span></span><br><span class="line"><span class="type">rt_err_t</span> <span class="title function_">rt_device_unregister</span><span class="params">(<span class="type">rt_device_t</span> dev)</span>; <span class="comment">//注销</span></span><br></pre></td></tr></table></figure></div><p>创建，注册之后，下一步就是使用这个设备，首先我们需要根据设备名称<strong>获取设备句柄，进而可以操作设备</strong>，获得设备句柄后，应用程序可使用如下函数<strong>对设备进行初始化</strong>操作，然后就可以<strong>通过设备句柄打开或者关闭设备</strong>。(如果调用时没有初始化设备，则会按默认参数初始化设备)</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">rt_device_t</span> <span class="title function_">rt_device_find</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* name)</span>; <span class="comment">//查找设备</span></span><br><span class="line"><span class="type">rt_err_t</span> <span class="title function_">rt_device_init</span><span class="params">(<span class="type">rt_device_t</span> dev)</span>; <span class="comment">//初始化设备</span></span><br><span class="line"><span class="type">rt_err_t</span> <span class="title function_">rt_device_open</span><span class="params">(<span class="type">rt_device_t</span> dev, <span class="type">rt_uint16_t</span> oflags)</span>; <span class="comment">//打开或关闭设备</span></span><br></pre></td></tr></table></figure></div><p>后期操作设备，也可以通过句柄来实现读写操作等,下面附rt-thread手册的一张补充图片:</p><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_065.webp" width="800"><h3 id="2-待补充"><a href="#2-待补充" class="headerlink" title="2. 待补充"></a>2. 待补充</h3><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><a class="link" href="https://www.bookstack.cn/books/rtthread-manual-doc">RT-Thread编程手册<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#x33;&#x31;&#52;&#54;&#55;&#48;&#x32;&#x33;&#x36;&#x32;&#64;&#x71;&#113;&#x2e;&#99;&#111;&#109;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">记录RT-Thread相关知识，为做相关实时操作系统相关嵌入式项目打基础</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="RT-Thread" scheme="https://blog.haozi-haozi.cn/tags/RT-Thread/"/>
    
  </entry>
  
  <entry>
    <title>Mpu6050——学习笔记</title>
    <link href="https://blog.haozi-haozi.cn/2025/03/14/embedded_mpu6050/"/>
    <id>https://blog.haozi-haozi.cn/2025/03/14/embedded_mpu6050/</id>
    <published>2025-03-14T13:25:30.000Z</published>
    <updated>2026-03-24T08:58:19.310Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-引入"><a href="#A-引入" class="headerlink" title="A 引入"></a>A 引入</h2><h2 id="A-1-模块和单片机型号说明"><a href="#A-1-模块和单片机型号说明" class="headerlink" title="A.1 模块和单片机型号说明"></a>A.1 模块和单片机型号说明</h2><p><strong>模块</strong>:mpu6050<br><strong>单片机</strong>:stm32f103c8t6<br><strong>函数库</strong>:标准库</p><p>当然，这只是一个大学生的学习笔记</p><h2 id="B-模块介绍"><a href="#B-模块介绍" class="headerlink" title="B 模块介绍"></a>B 模块介绍</h2><p>MPU6050是一个整合型的六轴运动处理模块，内部整合了3轴的陀螺仪和3轴的加速度计，还有一个温度传感器</p><p>常见的姿态传感器：</p><ol><li>六轴（加速度计+陀螺仪），其中加速度计测量的<strong>三轴的加速度</strong>，陀螺仪测量的是<strong>三轴的角速度</strong></li><li>九轴（加速度计+陀螺仪+磁力计） 磁力计用来解决六轴中偏航角的漂移，其中磁力计测量的时候<strong>三轴的磁场强度</strong></li><li>十轴（加速度计+陀螺仪+磁力计+气压计）其中气压计用来<strong>测量高度</strong></li></ol><h3 id="B-1-引脚定义"><a href="#B-1-引脚定义" class="headerlink" title="B.1 引脚定义"></a>B.1 引脚定义</h3><h3 id="陀螺仪引脚定义"><a href="#陀螺仪引脚定义" class="headerlink" title="陀螺仪引脚定义"></a>陀螺仪引脚定义</h3><table><thead><tr><th>引脚编号</th><th>定义</th><th>说明</th></tr></thead><tbody><tr><td>1</td><td>VCC</td><td>电源引脚，接3.3V或5V电源</td></tr><tr><td>2</td><td>GND</td><td>接地引脚</td></tr><tr><td>3</td><td>SCL</td><td>I2C时钟线</td></tr><tr><td>4</td><td>SDA</td><td>I2C数据线</td></tr><tr><td>5</td><td>XDA</td><td>辅助I2C数据线（通常未使用）</td></tr><tr><td>6</td><td>XCL</td><td>辅助I2C时钟线（通常未使用）</td></tr><tr><td>7</td><td>INT</td><td>中断引脚，用于输出中断信号</td></tr></tbody></table><p>MPU6050 通常使用 I2C 通信，通过操作内部的寄存器来配置 MPU6050 的工作模式并进行数据的读写。其中，XDA 和 XCL 引脚可以外接磁力计，用于扩展为九轴传感器，从而解决六轴传感器在偏航角测量中存在的漂移问题。<br>这种扩展方式可以显著提高姿态解算的精度，特别是在需要精确航向角的应用场景中。</p><h2 id="C-原理介绍"><a href="#C-原理介绍" class="headerlink" title="C 原理介绍"></a>C 原理介绍</h2><h3 id="C-1-姿态角介绍"><a href="#C-1-姿态角介绍" class="headerlink" title="C.1 姿态角介绍"></a>C.1 姿态角介绍</h3><p>姿态角是描述物体在三维空间中姿态的一组角度参数，通常包括三个角度：偏航角（Yaw）、俯仰角（Pitch）和横滚角（Roll）。它们分别对应物体绕垂直轴、横轴和纵轴的旋转角度，又称欧拉角。</p><p>如下图，以飞机为水平面，飞机朝向为x轴，机翼朝向为y轴，垂直方向为z轴</p><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_032.webp" width="400"><ol><li><p><strong>偏航角（Yaw）</strong><br>偏航角描述物体绕垂直轴（通常是Z轴）的旋转角度，反映物体的航向变化。偏航角在六轴传感器中容易受到漂移影响，通常需要通过外接磁力计来校正。</p></li><li><p><strong>俯仰角（Pitch）</strong><br>俯仰角描述物体绕横轴（通常是Y轴）的旋转角度，反映物体的前后倾斜程度。加速度计和陀螺仪的结合可以较为准确地测量俯仰角。</p></li><li><p><strong>横滚角（Roll）</strong><br>横滚角描述物体绕纵轴（通常是X轴）的旋转角度，反映物体的左右倾斜程度。与俯仰角类似，横滚角也可以通过加速度计和陀螺仪的融合来测量。</p></li></ol><p>在 MPU6050 中，通过加速度计和陀螺仪的原始数据，结合姿态解算算法（如卡尔曼滤波或互补滤波），可以计算出物体的姿态角。这些角度广泛应用于无人机、机器人、运动追踪等领域，用于实现姿态控制和导航。</p><h3 id="C-2-加速度计"><a href="#C-2-加速度计" class="headerlink" title="C.2 加速度计"></a>C.2 加速度计</h3><p>加速度计实际上是一个弹簧测力计，根据牛顿第二定律 F=ma，想要测量加速度 a，只需要找一个单位质量的物体，测量它所受的力 F 就行了。<br>在 MPU6050 中，加速度计通过检测内部微机械结构（MEMS）的位移来测量加速度。当模块受到外力作用时，内部的质量块会发生微小的位移，这种位移会通过电容变化或压电效应转换为电信号，从而计算出加速度值。</p><blockquote><p><strong>MPU6050 的加速度计可以测量三个方向（X、Y、Z）的加速度，后面表示为：ax，ay，az</strong></p></blockquote><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在静态情况下（基本只有重力加速度）</span></span><br><span class="line">roll  =  <span class="built_in">atan2</span> (ay,az)</span><br><span class="line">pitch = -<span class="built_in">atan2</span> (ax,az)</span><br><span class="line"><span class="comment">//但是在水平状态下，无法测量yaw（偏航角）</span></span><br></pre></td></tr></table></figure></div><p>当然，如果想增加稳定性可以使用下面的公式，对线性加速度稍鲁棒，误差较小</p><ol><li><p><strong>横滚角（Roll）</strong><br>横滚角描述物体绕 X 轴的旋转角度：<br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.827ex;" xmlns="http://www.w3.org/2000/svg" width="27.877ex" height="6.785ex" role="img" focusable="false" viewBox="0 -1749.5 12321.7 2999"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="52" d="M130 622Q123 629 119 631T103 634T60 637H27V683H202H236H300Q376 683 417 677T500 648Q595 600 609 517Q610 512 610 501Q610 468 594 439T556 392T511 361T472 343L456 338Q459 335 467 332Q497 316 516 298T545 254T559 211T568 155T578 94Q588 46 602 31T640 16H645Q660 16 674 32T692 87Q692 98 696 101T712 105T728 103T732 90Q732 59 716 27T672 -16Q656 -22 630 -22Q481 -16 458 90Q456 101 456 163T449 246Q430 304 373 320L363 322L297 323H231V192L232 61Q238 51 249 49T301 46H334V0H323Q302 3 181 3Q59 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM491 499V509Q491 527 490 539T481 570T462 601T424 623T362 636Q360 636 340 636T304 637H283Q238 637 234 628Q231 624 231 492V360H289Q390 360 434 378T489 456Q491 467 491 499Z"></path><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(736,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1236,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1514,0)"></path></g><g data-mml-node="mo" transform="translate(2069.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mi" transform="translate(3125.6,0)"><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z"></path><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(500,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(892,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1336,0)"></path><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(1725,0)"></path><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(2225,0)"></path></g><g data-mml-node="mo" transform="translate(5906.6,0)"><path data-c="2061" d=""></path></g><g data-mml-node="TeXAtom" data-mjx-texclass="ORD" transform="translate(6073.2,0)"><g data-mml-node="mrow"><g data-mml-node="mo" transform="translate(0 -0.5)"><path data-c="28" d="M758 -1237T758 -1240T752 -1249H736Q718 -1249 717 -1248Q711 -1245 672 -1199Q237 -706 237 251T672 1700Q697 1730 716 1749Q718 1750 735 1750H752Q758 1744 758 1741Q758 1737 740 1713T689 1644T619 1537T540 1380T463 1176Q348 802 348 251Q348 -242 441 -599T744 -1218Q758 -1237 758 -1240Z"></path></g><g data-mml-node="mfrac" transform="translate(792,0)"><g data-mml-node="msub" transform="translate(1853,755)"><g data-mml-node="mi"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(562,-150) scale(0.707)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="msqrt" transform="translate(220,-940.1)"><g transform="translate(1020,0)"><g data-mml-node="msubsup"><g data-mml-node="mi"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mn" transform="translate(562,289) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path></g><g data-mml-node="mi" transform="translate(562,-247) scale(0.707)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g></g><g data-mml-node="mo" transform="translate(1238.7,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msubsup" transform="translate(2238.9,0)"><g data-mml-node="mi"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mn" transform="translate(562,289) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path></g><g data-mml-node="mi" transform="translate(562,-247) scale(0.707)"><path data-c="1D467" d="M347 338Q337 338 294 349T231 360Q211 360 197 356T174 346T162 335T155 324L153 320Q150 317 138 317Q117 317 117 325Q117 330 120 339Q133 378 163 406T229 440Q241 442 246 442Q271 442 291 425T329 392T367 375Q389 375 411 408T434 441Q435 442 449 442H462Q468 436 468 434Q468 430 463 420T449 399T432 377T418 358L411 349Q368 298 275 214T160 106L148 94L163 93Q185 93 227 82T290 71Q328 71 360 90T402 140Q406 149 409 151T424 153Q443 153 443 143Q443 138 442 134Q425 72 376 31T278 -11Q252 -11 232 6T193 40T155 57Q111 57 76 -3Q70 -11 59 -11H54H41Q35 -5 35 -2Q35 13 93 84Q132 129 225 214T340 322Q352 338 347 338Z"></path></g></g></g><g data-mml-node="mo" transform="translate(0,70.1)"><path data-c="221A" d="M263 249Q264 249 315 130T417 -108T470 -228L725 302Q981 837 982 839Q989 850 1001 850Q1008 850 1013 844T1020 832V826L741 243Q645 43 540 -176Q479 -303 469 -324T453 -348Q449 -350 436 -350L424 -349L315 -96Q206 156 205 156L171 130Q138 104 137 104L111 130L263 249Z"></path></g><rect width="3204.5" height="60" x="1020" y="860.1"></rect></g><rect width="4424.5" height="60" x="120" y="220"></rect></g><g data-mml-node="mo" transform="translate(5456.5,0) translate(0 -0.5)"><path data-c="29" d="M33 1741Q33 1750 51 1750H60H65Q73 1750 81 1743T119 1700Q554 1207 554 251Q554 -707 119 -1199Q76 -1250 66 -1250Q65 -1250 62 -1250T56 -1249Q55 -1249 53 -1249T49 -1250Q33 -1250 33 -1239Q33 -1236 50 -1214T98 -1150T163 -1052T238 -910T311 -727Q443 -335 443 251Q443 402 436 532T405 831T339 1142T224 1438T50 1716Q33 1737 33 1741Z"></path></g></g></g></g></g></svg></mjx-container></p></li><li><p><strong>俯仰角（Pitch）</strong><br>俯仰角描述物体绕 Y 轴的旋转角度：<br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -4.118ex;" xmlns="http://www.w3.org/2000/svg" width="29.395ex" height="9.367ex" role="img" focusable="false" viewBox="0 -2320 12992.8 4140"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z"></path><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(681,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(959,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1348,0)"></path><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1792,0)"></path></g><g data-mml-node="mo" transform="translate(2625.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mi" transform="translate(3681.6,0)"><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z"></path><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(500,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(892,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1336,0)"></path><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(1725,0)"></path><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(2225,0)"></path></g><g data-mml-node="mo" transform="translate(6462.6,0)"><path data-c="2061" d=""></path></g><g data-mml-node="TeXAtom" data-mjx-texclass="ORD" transform="translate(6629.2,0)"><g data-mml-node="mrow"><g data-mml-node="mo"><path data-c="239B" d="M837 1154Q843 1148 843 1145Q843 1141 818 1106T753 1002T667 841T574 604T494 299Q417 -84 417 -609Q417 -641 416 -647T411 -654Q409 -655 366 -655Q299 -655 297 -654Q292 -652 292 -643T291 -583Q293 -400 304 -242T347 110T432 470T574 813T785 1136Q787 1139 790 1142T794 1147T796 1150T799 1152T802 1153T807 1154T813 1154H819H837Z" transform="translate(0,1166)"></path><path data-c="239D" d="M843 -635Q843 -638 837 -644H820Q801 -644 800 -643Q792 -635 785 -626Q684 -503 605 -363T473 -75T385 216T330 518T302 809T291 1093Q291 1144 291 1153T296 1164Q298 1165 366 1165Q409 1165 411 1164Q415 1163 416 1157T417 1119Q417 529 517 109T833 -617Q843 -631 843 -635Z" transform="translate(0,-1176)"></path><svg width="875" height="722" y="-111" x="0" viewBox="0 163 875 722"><path data-c="239C" d="M413 -9Q412 -9 407 -9T388 -10T354 -10Q300 -10 297 -9Q294 -8 293 -5Q291 5 291 127V300Q291 602 292 605L296 609Q298 610 366 610Q382 610 392 610T407 610T412 609Q416 609 416 592T417 473V127Q417 -9 413 -9Z" transform="scale(1,1.747)"></path></svg></g><g data-mml-node="mfrac" transform="translate(875,0)"><g data-mml-node="mrow" transform="translate(1409.5,676)"><g data-mml-node="mo"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="msub" transform="translate(778,0)"><g data-mml-node="mi"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(562,-150) scale(0.707)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g></g></g><g data-mml-node="msqrt" transform="translate(220,-1171.5)"><g transform="translate(1020,0)"><g data-mml-node="msubsup"><g data-mml-node="mi"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mn" transform="translate(562,289) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path></g><g data-mml-node="mi" transform="translate(562,-247) scale(0.707)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(1187.8,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msubsup" transform="translate(2188,0)"><g data-mml-node="mi"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mn" transform="translate(562,289) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path></g><g data-mml-node="mi" transform="translate(562,-247) scale(0.707)"><path data-c="1D467" d="M347 338Q337 338 294 349T231 360Q211 360 197 356T174 346T162 335T155 324L153 320Q150 317 138 317Q117 317 117 325Q117 330 120 339Q133 378 163 406T229 440Q241 442 246 442Q271 442 291 425T329 392T367 375Q389 375 411 408T434 441Q435 442 449 442H462Q468 436 468 434Q468 430 463 420T449 399T432 377T418 358L411 349Q368 298 275 214T160 106L148 94L163 93Q185 93 227 82T290 71Q328 71 360 90T402 140Q406 149 409 151T424 153Q443 153 443 143Q443 138 442 134Q425 72 376 31T278 -11Q252 -11 232 6T193 40T155 57Q111 57 76 -3Q70 -11 59 -11H54H41Q35 -5 35 -2Q35 13 93 84Q132 129 225 214T340 322Q352 338 347 338Z"></path></g></g></g><g data-mml-node="mo" transform="translate(0,1.5)"><path data-c="221A" d="M1001 1150Q1017 1150 1020 1132Q1020 1127 741 244L460 -643Q453 -650 436 -650H424Q423 -647 423 -645T421 -640T419 -631T415 -617T408 -594T399 -560T385 -512T367 -448T343 -364T312 -259L203 119L138 41L111 67L212 188L264 248L472 -474L983 1140Q988 1150 1001 1150Z"></path></g><rect width="3153.6" height="60" x="1020" y="1091.5"></rect></g><rect width="4373.6" height="60" x="120" y="220"></rect></g><g data-mml-node="mo" transform="translate(5488.6,0)"><path data-c="239E" d="M31 1143Q31 1154 49 1154H59Q72 1154 75 1152T89 1136Q190 1013 269 873T401 585T489 294T544 -8T572 -299T583 -583Q583 -634 583 -643T577 -654Q575 -655 508 -655Q465 -655 463 -654Q459 -653 458 -647T457 -609Q457 -58 371 340T100 1037Q87 1059 61 1098T31 1143Z" transform="translate(0,1166)"></path><path data-c="23A0" d="M56 -644H50Q31 -644 31 -635Q31 -632 37 -622Q69 -579 100 -527Q286 -228 371 170T457 1119Q457 1161 462 1164Q464 1165 520 1165Q575 1165 577 1164Q582 1162 582 1153T583 1093Q581 910 570 752T527 400T442 40T300 -303T89 -626Q78 -640 75 -642T61 -644H56Z" transform="translate(0,-1176)"></path><svg width="875" height="722" y="-111" x="0" viewBox="0 163 875 722"><path data-c="239F" d="M579 -9Q578 -9 573 -9T554 -10T520 -10Q466 -10 463 -9Q460 -8 459 -5Q457 5 457 127V300Q457 602 458 605L462 609Q464 610 532 610Q548 610 558 610T573 610T578 609Q582 609 582 592T583 473V127Q583 -9 579 -9Z" transform="scale(1,1.747)"></path></svg></g></g></g></g></g></svg></mjx-container></p></li></ol><blockquote><p><strong>加速度计具有静态稳定性，不具有动态稳定性</strong><br>因为假设在向x轴正方向加速的时候，加速度计在z轴和x轴上都有读数，但是它无法判断这是因为倾斜带来的还是加速带来的，而且也会有振动噪声产生，使结果不稳定</p></blockquote><h3 id="C-3-陀螺仪"><a href="#C-3-陀螺仪" class="headerlink" title="C.3 陀螺仪"></a>C.3 陀螺仪</h3><p>陀螺仪是一种利用角动量守恒原理来测量角速度的传感器。它可以检测物体绕三个轴（X、Y、Z）的旋转运动。在 MPU6050 中，陀螺仪通过微机械结构（MEMS）检测角速度，并输出对应的电信号。</p><p>需要注意的是，MPU6050 测量的是角速度（单位通常为 °/s），而不是角度。<br>如果需要得到角度值，可以通过对角速度进行积分来计算。</p><p>然而，由于积分过程中可能会累积误差（产生漂移），通常需要结合加速度计的数据，通过姿态解算算法（如卡尔曼滤波或互补滤波）来提高精度。</p><blockquote><p><strong>MPU6050 的加速度计可以测量三个方向（X、Y、Z）的角速度，后面表示为：gx，gy，gz</strong></p></blockquote><ol><li><p><strong>横滚角（Roll）</strong><br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="31.03ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 13715.4 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="52" d="M130 622Q123 629 119 631T103 634T60 637H27V683H202H236H300Q376 683 417 677T500 648Q595 600 609 517Q610 512 610 501Q610 468 594 439T556 392T511 361T472 343L456 338Q459 335 467 332Q497 316 516 298T545 254T559 211T568 155T578 94Q588 46 602 31T640 16H645Q660 16 674 32T692 87Q692 98 696 101T712 105T728 103T732 90Q732 59 716 27T672 -16Q656 -22 630 -22Q481 -16 458 90Q456 101 456 163T449 246Q430 304 373 320L363 322L297 323H231V192L232 61Q238 51 249 49T301 46H334V0H323Q302 3 181 3Q59 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM491 499V509Q491 527 490 539T481 570T462 601T424 623T362 636Q360 636 340 636T304 637H283Q238 637 234 628Q231 624 231 492V360H289Q390 360 434 378T489 456Q491 467 491 499Z"></path><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(736,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1236,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1514,0)"></path></g><g data-mml-node="mo" transform="translate(1792,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2181,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(2542,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(3208.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mtext" transform="translate(4264.6,0)"><path data-c="52" d="M130 622Q123 629 119 631T103 634T60 637H27V683H202H236H300Q376 683 417 677T500 648Q595 600 609 517Q610 512 610 501Q610 468 594 439T556 392T511 361T472 343L456 338Q459 335 467 332Q497 316 516 298T545 254T559 211T568 155T578 94Q588 46 602 31T640 16H645Q660 16 674 32T692 87Q692 98 696 101T712 105T728 103T732 90Q732 59 716 27T672 -16Q656 -22 630 -22Q481 -16 458 90Q456 101 456 163T449 246Q430 304 373 320L363 322L297 323H231V192L232 61Q238 51 249 49T301 46H334V0H323Q302 3 181 3Q59 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM491 499V509Q491 527 490 539T481 570T462 601T424 623T362 636Q360 636 340 636T304 637H283Q238 637 234 628Q231 624 231 492V360H289Q390 360 434 378T489 456Q491 467 491 499Z"></path><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(736,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1236,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1514,0)"></path></g><g data-mml-node="mo" transform="translate(6056.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(6445.6,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(7028.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(8029,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(8862,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(9223,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(9834.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msub" transform="translate(10834.4,0)"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g></g><g data-mml-node="mo" transform="translate(12021.1,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mi" transform="translate(12521.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(13354.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></svg></mjx-container></p></li><li><p><strong>俯仰角（Pitch）</strong><br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.667ex;" xmlns="http://www.w3.org/2000/svg" width="33.415ex" height="2.364ex" role="img" focusable="false" viewBox="0 -750 14769.4 1045"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z"></path><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(681,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(959,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1348,0)"></path><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1792,0)"></path></g><g data-mml-node="mo" transform="translate(2348,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2737,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(3098,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(3764.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mtext" transform="translate(4820.6,0)"><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z"></path><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(681,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(959,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1348,0)"></path><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1792,0)"></path></g><g data-mml-node="mo" transform="translate(7168.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(7557.6,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(8140.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(9141,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(9974,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(10335,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(10946.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msub" transform="translate(11946.4,0)"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(13075.1,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mi" transform="translate(13575.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(14408.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></svg></mjx-container></p></li><li><p><strong>偏航角（Yaw）</strong><br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="31.674ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 13999.7 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="59" d="M518 0Q497 3 374 3Q253 3 232 0H221V46H254Q313 47 321 58Q324 62 324 167V273L221 446Q117 620 114 623Q106 631 91 634T31 637H11V683H20Q29 680 148 680Q273 680 294 683H305V637H287Q239 636 236 621Q236 619 321 475L407 332L483 460Q502 492 527 534Q563 594 563 604Q563 632 517 637H508V683H517H525Q533 683 545 683T571 682T600 681T626 681Q695 681 731 683H738V637H723Q640 633 613 588Q612 587 517 427L425 273V169V95Q425 66 428 59T444 49Q459 46 506 46H528V0H518Z"></path><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(750,0)"></path><path data-c="77" d="M90 368Q84 378 76 380T40 385H18V431H24L43 430Q62 430 84 429T116 428Q206 428 221 431H229V385H215Q177 383 177 368Q177 367 221 239L265 113L339 328L333 345Q323 374 316 379Q308 384 278 385H258V431H264Q270 428 348 428Q439 428 454 431H461V385H452Q404 385 404 369Q404 366 418 324T449 234T481 143L496 100L537 219Q579 341 579 347Q579 363 564 373T530 385H522V431H529Q541 428 624 428Q692 428 698 431H703V385H697Q696 385 691 385T682 384Q635 377 619 334L559 161Q546 124 528 71Q508 12 503 1T487 -11H479Q460 -11 456 -4Q455 -3 407 133L361 267Q359 263 266 -4Q261 -11 243 -11H238Q225 -11 220 -3L90 368Z" transform="translate(1250,0)"></path></g><g data-mml-node="mo" transform="translate(1972,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2361,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(2722,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(3388.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mtext" transform="translate(4444.6,0)"><path data-c="59" d="M518 0Q497 3 374 3Q253 3 232 0H221V46H254Q313 47 321 58Q324 62 324 167V273L221 446Q117 620 114 623Q106 631 91 634T31 637H11V683H20Q29 680 148 680Q273 680 294 683H305V637H287Q239 636 236 621Q236 619 321 475L407 332L483 460Q502 492 527 534Q563 594 563 604Q563 632 517 637H508V683H517H525Q533 683 545 683T571 682T600 681T626 681Q695 681 731 683H738V637H723Q640 633 613 588Q612 587 517 427L425 273V169V95Q425 66 428 59T444 49Q459 46 506 46H528V0H518Z"></path><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(750,0)"></path><path data-c="77" d="M90 368Q84 378 76 380T40 385H18V431H24L43 430Q62 430 84 429T116 428Q206 428 221 431H229V385H215Q177 383 177 368Q177 367 221 239L265 113L339 328L333 345Q323 374 316 379Q308 384 278 385H258V431H264Q270 428 348 428Q439 428 454 431H461V385H452Q404 385 404 369Q404 366 418 324T449 234T481 143L496 100L537 219Q579 341 579 347Q579 363 564 373T530 385H522V431H529Q541 428 624 428Q692 428 698 431H703V385H697Q696 385 691 385T682 384Q635 377 619 334L559 161Q546 124 528 71Q508 12 503 1T487 -11H479Q460 -11 456 -4Q455 -3 407 133L361 267Q359 263 266 -4Q261 -11 243 -11H238Q225 -11 220 -3L90 368Z" transform="translate(1250,0)"></path></g><g data-mml-node="mo" transform="translate(6416.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(6805.6,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(7388.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(8389,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(9222,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(9583,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(10194.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msub" transform="translate(11194.4,0)"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D467" d="M347 338Q337 338 294 349T231 360Q211 360 197 356T174 346T162 335T155 324L153 320Q150 317 138 317Q117 317 117 325Q117 330 120 339Q133 378 163 406T229 440Q241 442 246 442Q271 442 291 425T329 392T367 375Q389 375 411 408T434 441Q435 442 449 442H462Q468 436 468 434Q468 430 463 420T449 399T432 377T418 358L411 349Q368 298 275 214T160 106L148 94L163 93Q185 93 227 82T290 71Q328 71 360 90T402 140Q406 149 409 151T424 153Q443 153 443 143Q443 138 442 134Q425 72 376 31T278 -11Q252 -11 232 6T193 40T155 57Q111 57 76 -3Q70 -11 59 -11H54H41Q35 -5 35 -2Q35 13 93 84Q132 129 225 214T340 322Q352 338 347 338Z"></path></g></g><g data-mml-node="mo" transform="translate(12305.5,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mi" transform="translate(12805.7,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(13638.7,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></svg></mjx-container></p></li></ol><ul><li><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="2.701ex" height="1.645ex" role="img" focusable="false" viewBox="0 -716 1194 727"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(833,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></svg></mjx-container>：时间间隔，单位为秒，通常由 MPU6050 的采样频率决定。</li></ul><blockquote><p><strong>陀螺仪具有动态稳定性，不具有静态稳定性</strong><br>因为陀螺仪可以准确测量物体的角速度，特别是在动态运动中表现出色。<br>但是，如果传感器静止下来，则没有加速度，便无法通过积分获得当前的姿态，尤其如果环境中还存在噪声，<strong>角速度会因为噪声而无法完全归零，经过积分会产生偏移</strong></p></blockquote><h4 id="所以当加速度计和陀螺仪一起使用的时候，通过互补滤波，卡尔曼滤波等，就可以得到相对稳定的姿态角"><a href="#所以当加速度计和陀螺仪一起使用的时候，通过互补滤波，卡尔曼滤波等，就可以得到相对稳定的姿态角" class="headerlink" title="所以当加速度计和陀螺仪一起使用的时候，通过互补滤波，卡尔曼滤波等，就可以得到相对稳定的姿态角"></a>所以当加速度计和陀螺仪一起使用的时候，通过互补滤波，卡尔曼滤波等，就可以得到相对稳定的姿态角</h4><h2 id="D-驱动代码"><a href="#D-驱动代码" class="headerlink" title="D 驱动代码"></a>D 驱动代码</h2><h3 id="D-1-如何用I2C读取MPU6050原始值"><a href="#D-1-如何用I2C读取MPU6050原始值" class="headerlink" title="D.1 如何用I2C读取MPU6050原始值"></a>D.1 如何用I2C读取MPU6050原始值</h3><p>MPU6050使用I2C协议通信，我们可以看它的寄存器手册知道如何配置采样率量程等参数，或者是读取角速度加速度等数据</p><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_033.webp" width="500"><p>举个栗子:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//假设我们I2C已经配置好了</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">MPU6050_WriteReg</span><span class="params">(<span class="type">uint8_t</span> RegAddress, <span class="type">uint8_t</span> Data)</span></span><br><span class="line">{</span><br><span class="line"> MyI2C_Start();                               <span class="comment">//I2C起始</span></span><br><span class="line"> MyI2C_SendByte(MPU6050_ADDRESS);             <span class="comment">//发送从机地址，读写位为0，表示即将写入</span></span><br><span class="line"> MyI2C_ReceiveAck();                          <span class="comment">//接收应答</span></span><br><span class="line"> MyI2C_SendByte(RegAddress);                  <span class="comment">//发送寄存器地址</span></span><br><span class="line"> MyI2C_ReceiveAck();                          <span class="comment">//接收应答</span></span><br><span class="line"> MyI2C_SendByte(Data);                        <span class="comment">//发送要写入寄存器的数据</span></span><br><span class="line"> MyI2C_ReceiveAck();                          <span class="comment">//接收应答</span></span><br><span class="line"> MyI2C_Stop();                                <span class="comment">//I2C终止</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint8_t</span> <span class="title function_">MPU6050_ReadReg</span><span class="params">(<span class="type">uint8_t</span> RegAddress)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">uint8_t</span> Data;<span class="comment">//定义接受的数据储存位置</span></span><br><span class="line"> </span><br><span class="line"> MyI2C_Start();                             <span class="comment">//I2C起始</span></span><br><span class="line"> MyI2C_SendByte(MPU6050_ADDRESS);           <span class="comment">//发送从机地址，读写位为0，表示即将写入</span></span><br><span class="line"> MyI2C_ReceiveAck();                        <span class="comment">//接收应答</span></span><br><span class="line"> MyI2C_SendByte(RegAddress);                <span class="comment">//发送寄存器地址</span></span><br><span class="line"> MyI2C_ReceiveAck();                        <span class="comment">//接收应答</span></span><br><span class="line"> </span><br><span class="line"> MyI2C_Start();                             <span class="comment">//I2C重复起始</span></span><br><span class="line"> MyI2C_SendByte(MPU6050_ADDRESS | <span class="number">0x01</span>);    <span class="comment">//发送从机地址，读写位为1，表示即将读取</span></span><br><span class="line"> MyI2C_ReceiveAck();                        <span class="comment">//接收应答</span></span><br><span class="line"> Data = MyI2C_ReceiveByte();                <span class="comment">//接收指定寄存器的数据</span></span><br><span class="line"> MyI2C_SendAck(<span class="number">1</span>);                          <span class="comment">//发送应答，给从机非应答，终止从机的数据输出</span></span><br><span class="line"> MyI2C_Stop();                              <span class="comment">//I2C终止</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> Data;<span class="comment">//返回读取到的值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>我们可以看到手册中的角速度值由0X3B和0X3C的高8位和低8位组成，所以假设我们要获得角速度的时候，要读取两次值，然后移位拼接得到真正的原始值，而16位二进制表示<strong>有符号的范围是-32768~32767</strong>，所以如果<strong>换算成+-250°/s <strong>就可以直接除以</strong>131</strong>，同理，如果读取的加速度，假设要<strong>换算成+-2g</strong>，就可以直接<strong>除以16384</strong></p><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_037.webp" width="800"><p>下面是手册中关于如何利用原始值换算成我们平常用的值</p><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_034.webp" width="500"><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_035.webp" width="500"><img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_036.webp" width="500"><h3 id="D-2-如何配置MPU6050"><a href="#D-2-如何配置MPU6050" class="headerlink" title="D.2 如何配置MPU6050"></a>D.2 如何配置MPU6050</h3><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//下面只是初始化的框架，具体函数内容和参数可以根据手册更改</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">MPU6050_Init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">{ </span><br><span class="line">    u8 res; </span><br><span class="line">    MPU6050_IIC_IO_Init(); <span class="comment">// 初始化 IIC 总线</span></span><br><span class="line">    mpu6050_write_reg(PWR_MGMT_1, <span class="number">0X80</span>); <span class="comment">// 复位 MPU6050</span></span><br><span class="line">    delay_ms(<span class="number">100</span>);</span><br><span class="line">    mpu6050_write_reg(PWR_MGMT_1, <span class="number">0X00</span>); <span class="comment">// 唤醒 MPU6050</span></span><br><span class="line">    MPU_Set_Gyro_Fsr(<span class="number">3</span>); <span class="comment">// 设置陀螺仪量程为 ±2000dps</span></span><br><span class="line">    MPU_Set_Accel_Fsr(<span class="number">0</span>); <span class="comment">// 设置加速度计量程为 ±2g</span></span><br><span class="line">    MPU_Set_Rate(<span class="number">100</span>); <span class="comment">// 设置采样率为 100Hz</span></span><br><span class="line">    mpu6050_write_reg(MPU_INT_EN_REG, <span class="number">0X00</span>); <span class="comment">// 关闭所有中断</span></span><br><span class="line">    mpu6050_write_reg(MPU_USER_CTRL_REG, <span class="number">0X00</span>); <span class="comment">// 关闭 I2C 主模式</span></span><br><span class="line">    mpu6050_write_reg(MPU_FIFO_EN_REG, <span class="number">0X00</span>); <span class="comment">// 关闭 FIFO</span></span><br><span class="line">    mpu6050_write_reg(MPU_INTBP_CFG_REG, <span class="number">0X80</span>); <span class="comment">// 配置 INT 引脚为低电平有效</span></span><br><span class="line">    res = mpu6050_read_reg(MPU_DEVICE_ID_REG); <span class="comment">// 读取设备 ID</span></span><br><span class="line">    <span class="keyword">if</span> (res == MPU_ADDR) <span class="comment">// 检查设备 ID 是否正确</span></span><br><span class="line">    {</span><br><span class="line">        mpu6050_write_reg(PWR_MGMT_1, <span class="number">0X01</span>); <span class="comment">// 设置时钟源为 PLL X 轴参考</span></span><br><span class="line">        mpu6050_write_reg(PWR_MGMT_2, <span class="number">0X00</span>); <span class="comment">// 启用加速度计和陀螺仪</span></span><br><span class="line">        MPU_Set_Rate(<span class="number">100</span>); <span class="comment">// 再次设置采样率为 100Hz</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="D-3-如何利用原始值解算姿态角"><a href="#D-3-如何利用原始值解算姿态角" class="headerlink" title="D.3 如何利用原始值解算姿态角"></a>D.3 如何利用原始值解算姿态角</h3><p>下面先介绍不使用DMP解算姿态角的方法，在前面我们已经可以通过加速度计和陀螺仪，得到ax，ay，az，gx，gy，gz，然后<strong>计算出2个roll，2个pitch和1个yaw</strong>，然后我们可以通过互补滤波来融合两组值来得到一个粗略的结果</p><p><strong>互补滤波公式：</strong></p><p>互补滤波是一种简单有效的姿态解算方法，通过结合加速度计和陀螺仪的数据，利用两者的优点来计算稳定的姿态角。其公式如下：</p><ol><li><p><strong>横滚角（Roll）</strong><br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="53.841ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 23797.6 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="52" d="M130 622Q123 629 119 631T103 634T60 637H27V683H202H236H300Q376 683 417 677T500 648Q595 600 609 517Q610 512 610 501Q610 468 594 439T556 392T511 361T472 343L456 338Q459 335 467 332Q497 316 516 298T545 254T559 211T568 155T578 94Q588 46 602 31T640 16H645Q660 16 674 32T692 87Q692 98 696 101T712 105T728 103T732 90Q732 59 716 27T672 -16Q656 -22 630 -22Q481 -16 458 90Q456 101 456 163T449 246Q430 304 373 320L363 322L297 323H231V192L232 61Q238 51 249 49T301 46H334V0H323Q302 3 181 3Q59 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM491 499V509Q491 527 490 539T481 570T462 601T424 623T362 636Q360 636 340 636T304 637H283Q238 637 234 628Q231 624 231 492V360H289Q390 360 434 378T489 456Q491 467 491 499Z"></path><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(736,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1236,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1514,0)"></path></g><g data-mml-node="mo" transform="translate(1792,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2181,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(2542,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(3208.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mi" transform="translate(4264.6,0)"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g><g data-mml-node="mo" transform="translate(5126.8,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mo" transform="translate(5627,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mtext" transform="translate(6016,0)"><path data-c="52" d="M130 622Q123 629 119 631T103 634T60 637H27V683H202H236H300Q376 683 417 677T500 648Q595 600 609 517Q610 512 610 501Q610 468 594 439T556 392T511 361T472 343L456 338Q459 335 467 332Q497 316 516 298T545 254T559 211T568 155T578 94Q588 46 602 31T640 16H645Q660 16 674 32T692 87Q692 98 696 101T712 105T728 103T732 90Q732 59 716 27T672 -16Q656 -22 630 -22Q481 -16 458 90Q456 101 456 163T449 246Q430 304 373 320L363 322L297 323H231V192L232 61Q238 51 249 49T301 46H334V0H323Q302 3 181 3Q59 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM491 499V509Q491 527 490 539T481 570T462 601T424 623T362 636Q360 636 340 636T304 637H283Q238 637 234 628Q231 624 231 492V360H289Q390 360 434 378T489 456Q491 467 491 499Z"></path><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(736,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1236,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1514,0)"></path></g><g data-mml-node="mo" transform="translate(7808,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(8197,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(8780.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(9780.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(10613.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(10974.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(11585.7,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msub" transform="translate(12585.9,0)"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g></g><g data-mml-node="mo" transform="translate(13772.6,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mi" transform="translate(14272.8,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(15105.8,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(15466.8,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(16078,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mo" transform="translate(17078.2,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mn" transform="translate(17467.2,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(18189.5,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(19189.7,0)"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g><g data-mml-node="mo" transform="translate(19829.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(20440.9,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="msub" transform="translate(20941.1,0)"><g data-mml-node="mtext"><path data-c="52" d="M130 622Q123 629 119 631T103 634T60 637H27V683H202H236H300Q376 683 417 677T500 648Q595 600 609 517Q610 512 610 501Q610 468 594 439T556 392T511 361T472 343L456 338Q459 335 467 332Q497 316 516 298T545 254T559 211T568 155T578 94Q588 46 602 31T640 16H645Q660 16 674 32T692 87Q692 98 696 101T712 105T728 103T732 90Q732 59 716 27T672 -16Q656 -22 630 -22Q481 -16 458 90Q456 101 456 163T449 246Q430 304 373 320L363 322L297 323H231V192L232 61Q238 51 249 49T301 46H334V0H323Q302 3 181 3Q59 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM491 499V509Q491 527 490 539T481 570T462 601T424 623T362 636Q360 636 340 636T304 637H283Q238 637 234 628Q231 624 231 492V360H289Q390 360 434 378T489 456Q491 467 491 499Z"></path><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(736,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1236,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1514,0)"></path></g><g data-mml-node="TeXAtom" transform="translate(1825,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mtext"><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(500,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(944,0)"></path></g></g></g></g></g></svg></mjx-container></p></li><li><p><strong>俯仰角（Pitch）</strong><br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.667ex;" xmlns="http://www.w3.org/2000/svg" width="57.483ex" height="2.364ex" role="img" focusable="false" viewBox="0 -750 25407.6 1045"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z"></path><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(681,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(959,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1348,0)"></path><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1792,0)"></path></g><g data-mml-node="mo" transform="translate(2348,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2737,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(3098,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(3764.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mi" transform="translate(4820.6,0)"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g><g data-mml-node="mo" transform="translate(5682.8,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mo" transform="translate(6183,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mtext" transform="translate(6572,0)"><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z"></path><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(681,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(959,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1348,0)"></path><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1792,0)"></path></g><g data-mml-node="mo" transform="translate(8920,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(9309,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(9892.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(10892.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(11725.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(12086.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(12697.7,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msub" transform="translate(13697.9,0)"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(14826.6,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mi" transform="translate(15326.8,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(16159.8,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(16520.8,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(17132,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mo" transform="translate(18132.3,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mn" transform="translate(18521.3,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(19243.5,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(20243.7,0)"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g><g data-mml-node="mo" transform="translate(20883.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(21494.9,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="msub" transform="translate(21995.1,0)"><g data-mml-node="mtext"><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z"></path><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(681,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(959,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1348,0)"></path><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1792,0)"></path></g><g data-mml-node="TeXAtom" transform="translate(2381,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mtext"><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(500,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(944,0)"></path></g></g></g></g></g></svg></mjx-container></p></li><li><p><strong>偏航角（Yaw）</strong><br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="31.674ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 13999.7 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="59" d="M518 0Q497 3 374 3Q253 3 232 0H221V46H254Q313 47 321 58Q324 62 324 167V273L221 446Q117 620 114 623Q106 631 91 634T31 637H11V683H20Q29 680 148 680Q273 680 294 683H305V637H287Q239 636 236 621Q236 619 321 475L407 332L483 460Q502 492 527 534Q563 594 563 604Q563 632 517 637H508V683H517H525Q533 683 545 683T571 682T600 681T626 681Q695 681 731 683H738V637H723Q640 633 613 588Q612 587 517 427L425 273V169V95Q425 66 428 59T444 49Q459 46 506 46H528V0H518Z"></path><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(750,0)"></path><path data-c="77" d="M90 368Q84 378 76 380T40 385H18V431H24L43 430Q62 430 84 429T116 428Q206 428 221 431H229V385H215Q177 383 177 368Q177 367 221 239L265 113L339 328L333 345Q323 374 316 379Q308 384 278 385H258V431H264Q270 428 348 428Q439 428 454 431H461V385H452Q404 385 404 369Q404 366 418 324T449 234T481 143L496 100L537 219Q579 341 579 347Q579 363 564 373T530 385H522V431H529Q541 428 624 428Q692 428 698 431H703V385H697Q696 385 691 385T682 384Q635 377 619 334L559 161Q546 124 528 71Q508 12 503 1T487 -11H479Q460 -11 456 -4Q455 -3 407 133L361 267Q359 263 266 -4Q261 -11 243 -11H238Q225 -11 220 -3L90 368Z" transform="translate(1250,0)"></path></g><g data-mml-node="mo" transform="translate(1972,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2361,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(2722,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(3388.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mtext" transform="translate(4444.6,0)"><path data-c="59" d="M518 0Q497 3 374 3Q253 3 232 0H221V46H254Q313 47 321 58Q324 62 324 167V273L221 446Q117 620 114 623Q106 631 91 634T31 637H11V683H20Q29 680 148 680Q273 680 294 683H305V637H287Q239 636 236 621Q236 619 321 475L407 332L483 460Q502 492 527 534Q563 594 563 604Q563 632 517 637H508V683H517H525Q533 683 545 683T571 682T600 681T626 681Q695 681 731 683H738V637H723Q640 633 613 588Q612 587 517 427L425 273V169V95Q425 66 428 59T444 49Q459 46 506 46H528V0H518Z"></path><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(750,0)"></path><path data-c="77" d="M90 368Q84 378 76 380T40 385H18V431H24L43 430Q62 430 84 429T116 428Q206 428 221 431H229V385H215Q177 383 177 368Q177 367 221 239L265 113L339 328L333 345Q323 374 316 379Q308 384 278 385H258V431H264Q270 428 348 428Q439 428 454 431H461V385H452Q404 385 404 369Q404 366 418 324T449 234T481 143L496 100L537 219Q579 341 579 347Q579 363 564 373T530 385H522V431H529Q541 428 624 428Q692 428 698 431H703V385H697Q696 385 691 385T682 384Q635 377 619 334L559 161Q546 124 528 71Q508 12 503 1T487 -11H479Q460 -11 456 -4Q455 -3 407 133L361 267Q359 263 266 -4Q261 -11 243 -11H238Q225 -11 220 -3L90 368Z" transform="translate(1250,0)"></path></g><g data-mml-node="mo" transform="translate(6416.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(6805.6,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(7388.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(8389,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(9222,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(9583,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(10194.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="msub" transform="translate(11194.4,0)"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D467" d="M347 338Q337 338 294 349T231 360Q211 360 197 356T174 346T162 335T155 324L153 320Q150 317 138 317Q117 317 117 325Q117 330 120 339Q133 378 163 406T229 440Q241 442 246 442Q271 442 291 425T329 392T367 375Q389 375 411 408T434 441Q435 442 449 442H462Q468 436 468 434Q468 430 463 420T449 399T432 377T418 358L411 349Q368 298 275 214T160 106L148 94L163 93Q185 93 227 82T290 71Q328 71 360 90T402 140Q406 149 409 151T424 153Q443 153 443 143Q443 138 442 134Q425 72 376 31T278 -11Q252 -11 232 6T193 40T155 57Q111 57 76 -3Q70 -11 59 -11H54H41Q35 -5 35 -2Q35 13 93 84Q132 129 225 214T340 322Q352 338 347 338Z"></path></g></g><g data-mml-node="mo" transform="translate(12305.5,0)"><path data-c="22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path></g><g data-mml-node="mi" transform="translate(12805.7,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(13638.7,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></svg></mjx-container></p></li></ol><p><strong>参数说明：</strong></p><ul><li><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="12.098ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 5347.4 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="52" d="M130 622Q123 629 119 631T103 634T60 637H27V683H202H236H300Q376 683 417 677T500 648Q595 600 609 517Q610 512 610 501Q610 468 594 439T556 392T511 361T472 343L456 338Q459 335 467 332Q497 316 516 298T545 254T559 211T568 155T578 94Q588 46 602 31T640 16H645Q660 16 674 32T692 87Q692 98 696 101T712 105T728 103T732 90Q732 59 716 27T672 -16Q656 -22 630 -22Q481 -16 458 90Q456 101 456 163T449 246Q430 304 373 320L363 322L297 323H231V192L232 61Q238 51 249 49T301 46H334V0H323Q302 3 181 3Q59 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM491 499V509Q491 527 490 539T481 570T462 601T424 623T362 636Q360 636 340 636T304 637H283Q238 637 234 628Q231 624 231 492V360H289Q390 360 434 378T489 456Q491 467 491 499Z"></path><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(736,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1236,0)"></path><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1514,0)"></path></g><g data-mml-node="mo" transform="translate(1792,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2181,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(2764.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(3764.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(4597.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(4958.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g></g></g></svg></mjx-container>、<mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="13.356ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 5903.4 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z"></path><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(681,0)"></path><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(959,0)"></path><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1348,0)"></path><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1792,0)"></path></g><g data-mml-node="mo" transform="translate(2348,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2737,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(3320.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(4320.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(5153.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(5514.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g></g></g></svg></mjx-container>、<mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="12.506ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 5527.4 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtext"><path data-c="59" d="M518 0Q497 3 374 3Q253 3 232 0H221V46H254Q313 47 321 58Q324 62 324 167V273L221 446Q117 620 114 623Q106 631 91 634T31 637H11V683H20Q29 680 148 680Q273 680 294 683H305V637H287Q239 636 236 621Q236 619 321 475L407 332L483 460Q502 492 527 534Q563 594 563 604Q563 632 517 637H508V683H517H525Q533 683 545 683T571 682T600 681T626 681Q695 681 731 683H738V637H723Q640 633 613 588Q612 587 517 427L425 273V169V95Q425 66 428 59T444 49Q459 46 506 46H528V0H518Z"></path><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(750,0)"></path><path data-c="77" d="M90 368Q84 378 76 380T40 385H18V431H24L43 430Q62 430 84 429T116 428Q206 428 221 431H229V385H215Q177 383 177 368Q177 367 221 239L265 113L339 328L333 345Q323 374 316 379Q308 384 278 385H258V431H264Q270 428 348 428Q439 428 454 431H461V385H452Q404 385 404 369Q404 366 418 324T449 234T481 143L496 100L537 219Q579 341 579 347Q579 363 564 373T530 385H522V431H529Q541 428 624 428Q692 428 698 431H703V385H697Q696 385 691 385T682 384Q635 377 619 334L559 161Q546 124 528 71Q508 12 503 1T487 -11H479Q460 -11 456 -4Q455 -3 407 133L361 267Q359 263 266 -4Q261 -11 243 -11H238Q225 -11 220 -3L90 368Z" transform="translate(1250,0)"></path></g><g data-mml-node="mo" transform="translate(1972,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2361,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(2944.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mi" transform="translate(3944.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(4777.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(5138.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g></g></g></svg></mjx-container>：上一时刻的姿态角。</li><li><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.464ex;" xmlns="http://www.w3.org/2000/svg" width="2.182ex" height="1.464ex" role="img" focusable="false" viewBox="0 -442 964.5 647"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g></g></g></g></svg></mjx-container>、<mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.667ex;" xmlns="http://www.w3.org/2000/svg" width="2.051ex" height="1.667ex" role="img" focusable="false" viewBox="0 -442 906.5 737"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container>、<mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.464ex;" xmlns="http://www.w3.org/2000/svg" width="2.011ex" height="1.464ex" role="img" focusable="false" viewBox="0 -442 888.8 647"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D454" d="M311 43Q296 30 267 15T206 0Q143 0 105 45T66 160Q66 265 143 353T314 442Q361 442 401 394L404 398Q406 401 409 404T418 412T431 419T447 422Q461 422 470 413T480 394Q480 379 423 152T363 -80Q345 -134 286 -169T151 -205Q10 -205 10 -137Q10 -111 28 -91T74 -71Q89 -71 102 -80T116 -111Q116 -121 114 -130T107 -144T99 -154T92 -162L90 -164H91Q101 -167 151 -167Q189 -167 211 -155Q234 -144 254 -122T282 -75Q288 -56 298 -13Q311 35 311 43ZM384 328L380 339Q377 350 375 354T369 368T359 382T346 393T328 402T306 405Q262 405 221 352Q191 313 171 233T151 117Q151 38 213 38Q269 38 323 108L331 118L384 328Z"></path></g><g data-mml-node="mi" transform="translate(510,-150) scale(0.707)"><path data-c="1D467" d="M347 338Q337 338 294 349T231 360Q211 360 197 356T174 346T162 335T155 324L153 320Q150 317 138 317Q117 317 117 325Q117 330 120 339Q133 378 163 406T229 440Q241 442 246 442Q271 442 291 425T329 392T367 375Q389 375 411 408T434 441Q435 442 449 442H462Q468 436 468 434Q468 430 463 420T449 399T432 377T418 358L411 349Q368 298 275 214T160 106L148 94L163 93Q185 93 227 82T290 71Q328 71 360 90T402 140Q406 149 409 151T424 153Q443 153 443 143Q443 138 442 134Q425 72 376 31T278 -11Q252 -11 232 6T193 40T155 57Q111 57 76 -3Q70 -11 59 -11H54H41Q35 -5 35 -2Q35 13 93 84Q132 129 225 214T340 322Q352 338 347 338Z"></path></g></g></g></g></svg></mjx-container>：陀螺仪在 X、Y、Z 轴上的角速度，单位通常为 °/s。</li><li><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="2.701ex" height="1.645ex" role="img" focusable="false" viewBox="0 -716 1194 727"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(833,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></svg></mjx-container>：时间间隔，单位为秒，通常由 MPU6050 的采样频率决定。</li><li><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g></g></g></svg></mjx-container>：滤波系数，范围为 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.312ex;" xmlns="http://www.w3.org/2000/svg" width="9.745ex" height="1.819ex" role="img" focusable="false" viewBox="0 -666 4307.1 804"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mn"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g><g data-mml-node="mo" transform="translate(777.8,0)"><path data-c="2264" d="M674 636Q682 636 688 630T694 615T687 601Q686 600 417 472L151 346L399 228Q687 92 691 87Q694 81 694 76Q694 58 676 56H670L382 192Q92 329 90 331Q83 336 83 348Q84 359 96 365Q104 369 382 500T665 634Q669 636 674 636ZM84 -118Q84 -108 99 -98H678Q694 -104 694 -118Q694 -130 679 -138H98Q84 -131 84 -118Z"></path></g><g data-mml-node="mi" transform="translate(1833.6,0)"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g><g data-mml-node="mo" transform="translate(2751.3,0)"><path data-c="2264" d="M674 636Q682 636 688 630T694 615T687 601Q686 600 417 472L151 346L399 228Q687 92 691 87Q694 81 694 76Q694 58 676 56H670L382 192Q92 329 90 331Q83 336 83 348Q84 359 96 365Q104 369 382 500T665 634Q669 636 674 636ZM84 -118Q84 -108 99 -98H678Q694 -104 694 -118Q694 -130 679 -138H98Q84 -131 84 -118Z"></path></g><g data-mml-node="mn" transform="translate(3807.1,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g></svg></mjx-container>，用于平衡加速度计和陀螺仪的权重。通常 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g></g></g></svg></mjx-container> 取值接近 0.98。</li></ul><p><strong>注释事项：</strong></p><ul><li>偏航角（Yaw）的计算通常需要磁力计来校正，因为加速度计无法提供水平状态下的偏航角信息。</li><li>滤波系数 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g></g></g></svg></mjx-container> 的选择需要根据实际应用场景调整，较大的 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g></g></g></svg></mjx-container> 值会更依赖陀螺仪，较小的 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g></g></g></svg></mjx-container> 值会更依赖加速度计。</li></ul><p><strong>滤波系数 α 的取值：</strong></p><p>滤波系数 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.448ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 640 453"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g></g></g></svg></mjx-container> 可以通过以下公式粗略计算：</p><p><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -1.738ex;" xmlns="http://www.w3.org/2000/svg" width="12.097ex" height="4.242ex" role="img" focusable="false" viewBox="0 -1107 5347 1875"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D6FC" d="M34 156Q34 270 120 356T309 442Q379 442 421 402T478 304Q484 275 485 237V208Q534 282 560 374Q564 388 566 390T582 393Q603 393 603 385Q603 376 594 346T558 261T497 161L486 147L487 123Q489 67 495 47T514 26Q528 28 540 37T557 60Q559 67 562 68T577 70Q597 70 597 62Q597 56 591 43Q579 19 556 5T512 -10H505Q438 -10 414 62L411 69L400 61Q390 53 370 41T325 18T267 -2T203 -11Q124 -11 79 39T34 156ZM208 26Q257 26 306 47T379 90L403 112Q401 255 396 290Q382 405 304 405Q235 405 183 332Q156 292 139 224T121 120Q121 71 146 49T208 26Z"></path></g><g data-mml-node="mo" transform="translate(917.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mfrac" transform="translate(1973.6,0)"><g data-mml-node="mi" transform="translate(1428.2,676)"><path data-c="1D70F" d="M39 284Q18 284 18 294Q18 301 45 338T99 398Q134 425 164 429Q170 431 332 431Q492 431 497 429Q517 424 517 402Q517 388 508 376T485 360Q479 358 389 358T299 356Q298 355 283 274T251 109T233 20Q228 5 215 -4T186 -13Q153 -13 153 20V30L203 192Q214 228 227 272T248 336L254 357Q254 358 208 358Q206 358 197 358T183 359Q105 359 61 295Q56 287 53 286T39 284Z"></path></g><g data-mml-node="mrow" transform="translate(220,-686)"><g data-mml-node="mi"><path data-c="1D70F" d="M39 284Q18 284 18 294Q18 301 45 338T99 398Q134 425 164 429Q170 431 332 431Q492 431 497 429Q517 424 517 402Q517 388 508 376T485 360Q479 358 389 358T299 356Q298 355 283 274T251 109T233 20Q228 5 215 -4T186 -13Q153 -13 153 20V30L203 192Q214 228 227 272T248 336L254 357Q254 358 208 358Q206 358 197 358T183 359Q105 359 61 295Q56 287 53 286T39 284Z"></path></g><g data-mml-node="mo" transform="translate(739.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mi" transform="translate(1739.4,0)"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(2572.4,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g><rect width="3133.4" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><p>其中：</p><ul><li><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.029ex;" xmlns="http://www.w3.org/2000/svg" width="1.17ex" height="1.005ex" role="img" focusable="false" viewBox="0 -431 517 444"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D70F" d="M39 284Q18 284 18 294Q18 301 45 338T99 398Q134 425 164 429Q170 431 332 431Q492 431 497 429Q517 424 517 402Q517 388 508 376T485 360Q479 358 389 358T299 356Q298 355 283 274T251 109T233 20Q228 5 215 -4T186 -13Q153 -13 153 20V30L203 192Q214 228 227 272T248 336L254 357Q254 358 208 358Q206 358 197 358T183 359Q105 359 61 295Q56 287 53 286T39 284Z"></path></g></g></g></svg></mjx-container>：陀螺仪的时间常数，单位为秒，粗略直接取0.1</li><li><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="2.701ex" height="1.645ex" role="img" focusable="false" viewBox="0 -716 1194 727"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(833,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></svg></mjx-container>：采样时间间隔，单位为秒。</li></ul><blockquote><p>对面上面这个粗略的计算方法，可以直接在stm32中计算实现，但是结果会有很大的误差和抖动，所以官方给我提供了一个方法：<br>DMP是mpu6050中传感器内部集成的硬件模块（Digital Motion Processor，数字运动处理器），<strong>它具有运算能力，通过获得原始数据，可能使用卡尔曼滤波和四元数解算姿态角，可以让我们获得更精准的结果，并且节省主控的资源</strong></p></blockquote><h3 id="D-4-如何使用DMP库读取解算后的姿态角"><a href="#D-4-如何使用DMP库读取解算后的姿态角" class="headerlink" title="D.4 如何使用DMP库读取解算后的姿态角"></a>D.4 如何使用DMP库读取解算后的姿态角</h3><h4 id="D-4-1-移植DMP库"><a href="#D-4-1-移植DMP库" class="headerlink" title="D.4.1 移植DMP库"></a>D.4.1 移植DMP库</h4><p>ps:咳咳，我自己没移植成功，还是先借用别人移植好的吧</p><p><strong>下面提供下载官方移植源码的方式：</strong></p><ol><li>打开官网：<a class="link" href="https://invensense.tdk.com/">https://invensense.tdk.com/<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li>找到相关固件界面<img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_038.webp" width="500"></li><li>滑到最下面找到eMD5.1.3或eMD6.12<img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_039.webp" width="500"></li><li>点击之后会让你登录，选择注册，注意会发送给你的邮箱check邮件，找不到的可以在垃圾邮箱里面翻一下</li><li>然后重新上面的流程就可以下载了，如果下载速度过慢可以试试魔法</li><li>下载完之后在文件中选中下面这几个文件，就可以开始移植了<img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_041.webp" width="500"></li><li>参考可以看MSP430的驱动，就是下图文件<img lazyload="" src="/images/loading.svg" data-src="\img\blog_word\Embedded_1\word_00001_040.webp" width="500"></li></ol><h4 id="D-4-1-使用DMP库"><a href="#D-4-1-使用DMP库" class="headerlink" title="D.4.1 使用DMP库"></a>D.4.1 使用DMP库</h4><p>跳过最难的配置dmp环节,下面是或得姿态角的关键代码</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 获取 DMP 解算后的姿态角（注意：需要先初始化 DMP 并正确配置）</span></span><br><span class="line"><span class="comment">// pitch: 俯仰角，单位：0.1°，范围：-90.0° &lt;---&gt; +90.0°</span></span><br><span class="line"><span class="comment">// roll: 横滚角，单位：0.1°，范围：-180.0° &lt;---&gt; +180.0°</span></span><br><span class="line"><span class="comment">// yaw: 偏航角，单位：0.1°，范围：-180.0° &lt;---&gt; +180.0°</span></span><br><span class="line"><span class="comment">// 返回值：0 表示成功，1 表示读取 FIFO 数据失败，2 表示未检测到四元数数据</span></span><br><span class="line">u8 <span class="title function_">MPU6050_DMP_Get_Data</span><span class="params">(<span class="type">float</span> *pitch, <span class="type">float</span> *roll, <span class="type">float</span> *yaw)</span> <span class="comment">// 每 5ms 读取一次，200Hz 采样率</span></span><br><span class="line">{</span><br><span class="line">    <span class="type">float</span> q0 = <span class="number">1.0f</span>, q1 = <span class="number">0.0f</span>, q2 = <span class="number">0.0f</span>, q3 = <span class="number">0.0f</span>; <span class="comment">// 初始化四元数</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> sensor_timestamp;                  <span class="comment">// 传感器时间戳</span></span><br><span class="line">    <span class="type">short</span> gyro[<span class="number">3</span>], accel[<span class="number">3</span>], sensors;                <span class="comment">// 陀螺仪、加速度计数据和传感器标志</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> more;                              <span class="comment">// FIFO 中是否有更多数据</span></span><br><span class="line">    <span class="type">long</span> quat[<span class="number">4</span>];                                    <span class="comment">// 四元数数据（Q30 格式）</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从 FIFO 中读取数据（包括陀螺仪、加速度计和四元数）</span></span><br><span class="line">    <span class="keyword">if</span> (dmp_read_fifo(gyro, accel, quat, &amp;sensor_timestamp, &amp;sensors, &amp;more)) </span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>; <span class="comment">// 如果读取失败，返回 1</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* </span></span><br><span class="line"><span class="comment">     * 陀螺仪和加速度计数据以芯片坐标系和硬件单位写入 FIFO。</span></span><br><span class="line"><span class="comment">     * 这种行为确保 dmp_read_fifo 和 mpu_read_fifo 的输出一致。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查是否包含四元数数据</span></span><br><span class="line">    <span class="keyword">if</span> (sensors &amp; INV_WXYZ_QUAT) </span><br><span class="line">    {</span><br><span class="line">        <span class="comment">// 将 Q30 格式的四元数转换为浮点数</span></span><br><span class="line">        q0 = quat[<span class="number">0</span>] / q30;</span><br><span class="line">        q1 = quat[<span class="number">1</span>] / q30;</span><br><span class="line">        q2 = quat[<span class="number">2</span>] / q30;</span><br><span class="line">        q3 = quat[<span class="number">3</span>] / q30;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用四元数计算俯仰角（pitch）、横滚角（roll）和偏航角（yaw）</span></span><br><span class="line">        *pitch = <span class="built_in">asin</span>(<span class="number">-2</span> * q1 * q3 + <span class="number">2</span> * q0 * q2) * <span class="number">57.3</span>; <span class="comment">// pitch = arcsin(-2 * q1 * q3 + 2 * q0 * q2)</span></span><br><span class="line">        *roll  = <span class="built_in">atan2</span>(<span class="number">2</span> * q2 * q3 + <span class="number">2</span> * q0 * q1, <span class="number">-2</span> * q1 * q1 - <span class="number">2</span> * q2 * q2 + <span class="number">1</span>) * <span class="number">57.3</span>; <span class="comment">// roll = atan2(...)</span></span><br><span class="line">        *yaw   = <span class="built_in">atan2</span>(<span class="number">2</span> * (q1 * q2 + q0 * q3), q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * <span class="number">57.3</span>; <span class="comment">// yaw = atan2(...)</span></span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span> </span><br><span class="line">        <span class="keyword">return</span> <span class="number">2</span>; <span class="comment">// 如果未检测到四元数数据，返回 2</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">// 成功返回 0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><blockquote><p>这个函数通过 <code>dmp_read_fifo</code> 函数从 FIFO 中读取陀螺仪、加速度计和四元数数据，然后根据公式计算出姿态角，然后调用这个函数在我们需用使用的模块即可</p></blockquote><p><strong>总体的流程就是：</strong></p><pre class="mermaid">  graph TD;    A[配置DMP] --&gt; B[DMP读取原始值]    B --&gt; C[DMP进行融合计算]    C --&gt; D[DMP输出四元数]    D --&gt; E[主控读取四元数]    E --&gt; F[主控计算出姿态角]    F --&gt; G[使用计算的姿态角]</pre><h2 id="E-如何外接磁力计"><a href="#E-如何外接磁力计" class="headerlink" title="E 如何外接磁力计"></a>E 如何外接磁力计</h2><p>ps：还在学习ing</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><a class="link" href="https://www.bilibili.com/video/BV1B4421c7QW/?share_source=copy_web&vd_source=5c3c7b44ba8419d1825199fbef6f9289">铁头山羊stm32入门教程6.4 MPU6050(上)<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://blog.csdn.net/qq_44852376/article/details/130470815?fromshare=blogdetail&sharetype=blogdetail&sharerId=130470815&sharerefer=PC&sharesource=hun_hao&sharefrom=from_link9">MPU6050 6轴姿态传感器的分析与使用（一）<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="文件地址"><a href="#文件地址" class="headerlink" title="文件地址"></a>文件地址</h3><blockquote><p>完成后上传</p></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link" href="mailto:&#51;&#x31;&#52;&#54;&#x37;&#48;&#50;&#x33;&#x36;&#x32;&#64;&#x71;&#113;&#x2e;&#x63;&#111;&#x6d;">邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">mpu6050陀螺仪模块-学习笔记</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="Mpu6050" scheme="https://blog.haozi-haozi.cn/tags/Mpu6050/"/>
    
  </entry>
  
  <entry>
    <title>HC-05蓝牙模块——学习笔记</title>
    <link href="https://blog.haozi-haozi.cn/2025/03/11/embedded_HC_05/"/>
    <id>https://blog.haozi-haozi.cn/2025/03/11/embedded_HC_05/</id>
    <published>2025-03-11T07:25:30.000Z</published>
    <updated>2026-03-24T08:57:46.291Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-引入"><a href="#A-引入" class="headerlink" title="A 引入"></a>A 引入</h2><h2 id="A-1-模块和单片机型号说明"><a href="#A-1-模块和单片机型号说明" class="headerlink" title="A.1 模块和单片机型号说明"></a>A.1 模块和单片机型号说明</h2><p><strong>模块</strong>:HC-05 + USB转TTL<br><strong>单片机</strong>:stm32f103c8t6<br><strong>函数库</strong>:标准库</p><p>当然，这只是一个大学生的学习笔记</p><h2 id="B-模块介绍"><a href="#B-模块介绍" class="headerlink" title="B 模块介绍"></a>B 模块介绍</h2><blockquote><p>蓝牙模块常用的分为<strong>HC-05</strong>和<strong>HC-06</strong>,二者最主要的区别就是HC-05可以当<strong>主机(主从一体)</strong>,而HC-06只能当从机.<br>因为我学习的是双向通信,所以本文主要以<strong>HC-05</strong>介绍功能和使用方法</p></blockquote><h3 id="B-1-HC-05简介"><a href="#B-1-HC-05简介" class="headerlink" title="B.1 HC-05简介"></a>B.1 HC-05简介</h3><blockquote><p>HC-05 蓝牙串口通信模块，是基于 Bluetooth Specification V2.0 带 EDR 蓝牙协议的<br>数传模块。无线工作频段为 2.4GHz ISM，调制方式是 GFSK。模块最大发射功率为 4dBm，<br>接收灵敏度-85dBm，板载 PCB 天线，可以实现 10 米距离通信。<br>模块采用邮票孔封装方式，模块大小 27mm×13mm×2mm，方便客户嵌入应用系统之<br>内，自带 LED 灯，可直观判断蓝牙的连接状态。<br>模块采用 CSR 的 BC417 芯片，支持 AT 指令，用户可根据需要更改角色（主、从模式）<br>以及串口波特率、设备名称等参数，使用灵活。</p></blockquote><h3 id="B-2-引脚定义"><a href="#B-2-引脚定义" class="headerlink" title="B.2 引脚定义"></a>B.2 引脚定义</h3><table><thead><tr><th>引脚编号</th><th>定义</th><th>说明</th></tr></thead><tbody><tr><td>1</td><td>TXD</td><td>URAT 输出口，用于发送数据</td></tr><tr><td>2</td><td>RXD</td><td>URAT 输入口，用于接收数据</td></tr><tr><td>3</td><td>VCC</td><td>电源，接3.3V或5V（根据模块规格）</td></tr><tr><td>4</td><td>GND</td><td>接地</td></tr><tr><td>5</td><td>EN（KEY）</td><td>AT指令设置脚，高电平时进入AT模式，低电平时为正常工作模式；与按键功能相同</td></tr><tr><td>6</td><td>STATE</td><td>蓝牙状态引出脚，未连接时输出低电平，连接时输出高电平</td></tr></tbody></table><h3 id="B-3-补充说明"><a href="#B-3-补充说明" class="headerlink" title="B.3 补充说明"></a>B.3 补充说明</h3><blockquote><p>1.如封面图，这是一个封装好的模块，输入电压为3.2~6V，模块内置降压到3.3V，注意看买的模块的商家说明<br>2.通信距离一般为10米，越远越不保证通信质量<br>3.当配对后蓝牙可以当做全双工串口通信使用，不需用了解具体的蓝牙协议<br>4.默认值：从机，波特率9600，停止位1，校验位N，如果想要更改可以使用<strong>AT指令</strong></p></blockquote><h2 id="C-使用方法"><a href="#C-使用方法" class="headerlink" title="C 使用方法"></a>C 使用方法</h2><h3 id="C-1-单向通信"><a href="#C-1-单向通信" class="headerlink" title="C.1 单向通信"></a>C.1 单向通信</h3><p>当进行单向通信的时候，我们可以将它直接与stm32的IO口连接，并配置好串口通信（<strong>注意要与HC-05串口配置值相同，波特率停止位等，如果要更改可以看后面的AT指令</strong>），然后让手机或者其它外设连接蓝牙，利用蓝牙调试助手等工具发送消息，就可以在stm32上面利用串口中断接受到发送的数据。<br><strong>由上面的操作便可以远处发送指令点灯，遥控车等</strong></p><blockquote><p>当我们对蓝牙进行调试的时候，可以直接用USB转TTL连接蓝牙模块,蓝牙TX接串口RX，蓝牙RX接串口TX，GND相连，VCC接3.3V或5V，然后电脑打开串口助手调好参数，手机连接蓝牙调试助手连接蓝牙（默认名称:HC-05），就可以手机发送电脑接收或者交换。</p></blockquote><h3 id="C-2-AT指令"><a href="#C-2-AT指令" class="headerlink" title="C.2 AT指令"></a>C.2 AT指令</h3><p>在使用双向通信之前，要先了解AT指令，这是利用串口发送指令对蓝牙模块配置的过程，通过<strong>AT指令可以设置设备名称，设备配对码，设备模式，设备串口参数…..</strong></p><h4 id="C-2-1-常用的AT指令"><a href="#C-2-1-常用的AT指令" class="headerlink" title="C.2.1 常用的AT指令"></a>C.2.1 常用的AT指令</h4><p>下面是比较常用的几条AT指令:</p><table><thead><tr><th>指令</th><th>作用</th><th>回复</th><th>参数说明</th></tr></thead><tbody><tr><td><code>AT</code></td><td>测试指令</td><td><code>OK</code></td><td>无</td></tr><tr><td><code>AT+ORGL</code></td><td>恢复默认状态</td><td><code>OK</code></td><td>无</td></tr><tr><td><code>AT+ADDR</code></td><td>获取模块蓝牙地址</td><td><code>+ADDR:&lt;address&gt;</code></td><td>无</td></tr><tr><td><code>AT+NAME=&lt;Param&gt;</code></td><td>设置设备名称</td><td><code>OK</code></td><td><code>&lt;Param&gt;</code>：设备名称（如<code>&quot;HC-05&quot;</code>）</td></tr><tr><td><code>AT+NAME?</code></td><td>查询设备名称</td><td><code>+NAME:&lt;name&gt;</code></td><td>无</td></tr><tr><td><code>AT+ROLE=&lt;Param&gt;</code></td><td>设置模块角色</td><td><code>OK</code></td><td><code>&lt;Param&gt;</code>：<code>0</code>（从模式），<code>1</code>（主模式），<code>2</code>（回环模式）</td></tr><tr><td><code>AT+ROLE?</code></td><td>查询模块角色</td><td><code>+ROLE:&lt;role&gt;</code></td><td>无</td></tr><tr><td><code>AT+PSWD=&lt;Param&gt;</code></td><td>设置配对码</td><td><code>OK</code></td><td><code>&lt;Param&gt;</code>：配对码（如<code>&quot;1234&quot;</code>）</td></tr><tr><td><code>AT+PSWD?</code></td><td>查询配对码</td><td><code>+PSWD:&lt;password&gt;</code></td><td>无</td></tr><tr><td><code>AT+UART=&lt;Param&gt;,&lt;Param2&gt;,&lt;Param3&gt;</code></td><td>设置串口参数</td><td><code>OK</code></td><td><code>&lt;Param&gt;</code>：波特率（如<code>9600</code>），<code>&lt;Param2&gt;</code>：停止位（<code>0</code>-1位，<code>1</code>-2位），<code>&lt;Param3&gt;</code>：校验位（<code>0</code>-无校验，<code>1</code>-奇校验，<code>2</code>-偶校验）</td></tr><tr><td><code>AT+UART?</code></td><td>查询串口参数</td><td><code>+UART:&lt;baud&gt;,&lt;stop&gt;,&lt;parity&gt;</code></td><td>无</td></tr><tr><td><code>AT+CMODE=&lt;Param&gt;</code></td><td>设置连接模式</td><td><code>OK</code></td><td><code>&lt;Param&gt;</code>：<code>0</code>（指定地址连接），<code>1</code>（任意地址连接），<code>2</code>（回环模式）</td></tr><tr><td><code>AT+CMODE?</code></td><td>查询连接模式</td><td><code>+CMODE:&lt;mode&gt;</code></td><td>无</td></tr><tr><td><code>AT+BIND=&lt;Param&gt;</code></td><td>设置绑定地址</td><td><code>OK</code></td><td><code>&lt;Param&gt;</code>：绑定地址（如<code>&quot;98D3,31,F60B0A&quot;</code>）</td></tr><tr><td><code>AT+BIND?</code></td><td>查询绑定地址</td><td><code>+BIND:&lt;address&gt;</code></td><td>无</td></tr></tbody></table><blockquote><p><strong>模块进入AT模式的该方法</strong><br>1、模块上电，未配对情况下就是 AT 模式，波特率为模块本身的波特率，默认：9600，发送 AT 指令时需要置高一<br>次 PIO11。<br>2、PIO11 置高电平后，再给模块上电，此时模块进入 AT 模式，波特率固定为：38400，可以直接发送 AT 指令<br>注意：这里的PIO11 置高电平就是那个按钮（长按按钮上电，就会将波特率调到默认的38400）</p></blockquote><h4 id="C-2-2-调试AT指令过程中的问题"><a href="#C-2-2-调试AT指令过程中的问题" class="headerlink" title="C.2.2 调试AT指令过程中的问题"></a>C.2.2 调试AT指令过程中的问题</h4><blockquote><p><strong>问题1：发送指令没有反应</strong><br><strong>原因1：</strong> 在发送AT指令之前，要先将34引脚置高电平，即蓝牙模块上面有个按钮要先按一下，个别指令需用一直按着然后发送<br><strong>原因2：</strong> 可能蓝牙模块与你的串口的波特率不一致，请选择尝试9600和38400波特率，如果都不可以，选择长按按钮复重置到38400的波特率再重新尝试<br><strong>原因3</strong> AT指令的结尾为”&#x2F;r&#x2F;n”,如果你发送的指令没有，可能会没有反应，同事注意符号都为中文</p></blockquote><h3 id="C-3-双向通信"><a href="#C-3-双向通信" class="headerlink" title="C.3 双向通信"></a>C.3 双向通信</h3><h4 id="C-3-1-双蓝牙通信配置过程"><a href="#C-3-1-双蓝牙通信配置过程" class="headerlink" title="C.3.1 双蓝牙通信配置过程"></a>C.3.1 双蓝牙通信配置过程</h4><blockquote><p><strong>大家配置的时候，可以在每个操作之后再使用查询指令，确认自己设置的是否正确</strong></p></blockquote><p>建议先对从机操作，下面以我配置过程举个例子：</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">*********************从机配置*********************</span><br><span class="line">AT+ORGL                   //从机初始化,注意使用完这个指令之后波特率会更改为38400</span><br><span class="line">AT+ROLE=0                 //从机设置角色模式</span><br><span class="line">AT+PSWD=1977              //从机设置配对码，有些蓝牙模块可能是6位密码</span><br><span class="line">AT+ADDR?                  //查询从机地址，后面需用使用这个值，假设为3A:CE:59:C0:C0:34</span><br><span class="line">AT+UART=9600,0,0          //从机设置串口通信</span><br><span class="line"></span><br><span class="line">*********************主机配置*********************</span><br><span class="line">AT+ORGL                   //主机初始化,注意使用完这个指令之后波特率会更改为38400</span><br><span class="line">AT+ROLE=1                 //主机设置角色模式</span><br><span class="line">AT+PSWD=1977              //主机设置配对码，注意要和从机相同</span><br><span class="line">AT+BIND=3A,CE,59,C0,C0,34 //主机设置绑定地址，注意冒号更改成英文逗号</span><br><span class="line">AT+CMODE=0                //主机设置连接模式，这样才能自动连接绑定的地址</span><br><span class="line">AT+UART=9600,0,0          //主机设置串口通信</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>当全部配置完毕之后，将两个蓝牙模块连接电源，注意EA&#x2F;KEY引脚不接，然后二者则会自动配对，可以互相发送接受指令.</p><h4 id="C-3-2-配置过程遇到的问题"><a href="#C-3-2-配置过程遇到的问题" class="headerlink" title="C.3.2 配置过程遇到的问题"></a>C.3.2 配置过程遇到的问题</h4><blockquote><p><strong>问题1：设置密码的时候返回FAIL</strong><br><strong>原因1：</strong> 设置密码的时候注意密码如果有双引号，不要使用中文输入法，也可以尝试不使用双引号<br><strong>原因2：</strong> 可以询问你的商家给你的模块密码是几位，大多数的HC-05都是四位密码，默认“1234”,但是也有例外，比如我买的密码是6位的，所以需要更改设置密码的长度</p></blockquote><blockquote><p><strong>问题2：全部设置完了，但是两个蓝牙无法互相连接</strong><br><strong>原因1：</strong> 这里先说明连接成功后的状态，<br><strong>连线前:</strong><br>主机未记录从机地址时，快闪；<br>主机记录从机地址时，慢闪；<br>从机快闪。<br><strong>连线后:</strong><br>LED 两闪一停。<br>先置高 KEY 脚再给模块上电，进入 AT 指令模式，波特率固定为 38400，<br>LED 每 2 秒亮 1 秒。<br><strong>如果无法连接，先检查电路电压是否稳定，或者有没有干扰什么的</strong><br><strong>原因2：</strong> 在设置设置绑定地址的时候，注意地址之间的冒号要更改成英文的逗号，不然可能会出现这个问题<br><strong>原因3：</strong> 注意主机和从机的模式要设置正确，并且主机的连接模式最好设置成0，即指定地址连接（AT+CMODE&#x3D;0）</p></blockquote><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><p>1.HC-05 蓝牙 2.0 串口模块 用户手册</p></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link"   href="mailto:&#51;&#49;&#52;&#x36;&#55;&#x30;&#50;&#51;&#x36;&#50;&#64;&#113;&#113;&#46;&#x63;&#x6f;&#109;" >邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">HC-05蓝牙模块-学习笔记</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="HC-05" scheme="https://blog.haozi-haozi.cn/tags/HC-05/"/>
    
  </entry>
  
  <entry>
    <title>Qt——写一个自己的串口调试助手</title>
    <link href="https://blog.haozi-haozi.cn/2024/12/09/Qt_Serial/"/>
    <id>https://blog.haozi-haozi.cn/2024/12/09/Qt_Serial/</id>
    <published>2024-12-09T02:07:30.000Z</published>
    <updated>2026-03-24T08:56:45.878Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-引入"><a href="#A-引入" class="headerlink" title="A 引入"></a>A 引入</h2><p><strong>软件功能:</strong></p><ol><li>自动扫描电脑连接的串口</li><li>接收消息和发送消息，实现不同编码的通信，比如hex，utf8，gbk</li><li>能够设置串口的一些配置，比如波特率，停止位等</li></ol><p><strong>所需环境和软件工具:</strong></p><ol><li>Qt Creator 4.12.2</li><li>Qt 5.12.9</li><li>现成的能用串口助手，我用的是江科大的串口助手</li><li>虚拟串口工具（用于创建一对虚拟串口），我用的是vpsd</li><li>电脑为win11 64bit</li></ol><p><strong>最终界面:</strong></p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_022.webp"                      width="800"                 ><h2 id="B-具体实现"><a href="#B-具体实现" class="headerlink" title="B 具体实现"></a>B 具体实现</h2><h3 id="B-1界面UI布局"><a href="#B-1界面UI布局" class="headerlink" title="B.1界面UI布局"></a>B.1界面UI布局</h3><blockquote><p>在“mainwindow.ui”中布局如下,因为界面比较简单就不写布局的代码了</p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_023.webp"                      width="800"                 ></blockquote><blockquote><p>然后可以在波特率，停止位等下拉框中填入常用的量，后面代码中会使用</p></blockquote><h3 id="B-2实现打开软件时自动读取可用串口"><a href="#B-2实现打开软件时自动读取可用串口" class="headerlink" title="B.2实现打开软件时自动读取可用串口"></a>B.2实现打开软件时自动读取可用串口</h3><blockquote><p>从Qt 5.1版本开始，Qt就有了自己的串口通讯类，之前版本需要使用第三方的串口通信类才行。<br>要想使用串口通信类，需要在 .pro 文件中添加 Qt +&#x3D; serialport</p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_024.webp"                      width="800"                 ></blockquote><blockquote><p>同时要添加头文件:<br>#include “QSerialPort”<br>#include “QSerialPortInfo”</p></blockquote><p>显示串口COM的下拉框是一个QcomboBox控件,名称为ui-&gt;com_combox我们需要往这个控件中传入电脑连接的所有串口号的列表，所以先通过串口类中的QSerialPortInfo::availablePorts() 可以获得一个 QList ，List中的每一项 QSerialPortInfo 代表一个串口实例，该类中保存了系统中已有串口的端口名称、系统位置、描述和供应商等信息。</p><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//创建一个串口的对象</span></span><br><span class="line">my_serialPort  = <span class="keyword">new</span> QSerialPort;</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建获取串口号列表</span></span><br><span class="line">QStringList my_serialPortName = <span class="built_in">getPortNameList</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">//清空下拉栏</span></span><br><span class="line">ui-&gt;com_combox-&gt;<span class="built_in">clear</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">//添加查询到的串口到窗口中</span></span><br><span class="line">ui-&gt;com_combox-&gt;<span class="built_in">addItems</span>(my_serialPortName);</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>下面实现一个打开软件时获取串口的函数getPortNameList()</p><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QSerialPort&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QSerialPortInfo&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//获取串口号</span></span><br><span class="line"><span class="function">QStringList <span class="title">MainWindow::getPortNameList</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    QStringList my_serialPortName;</span><br><span class="line">    <span class="keyword">auto</span> ports  = QSerialPortInfo::<span class="built_in">availablePorts</span>();</span><br><span class="line">    <span class="keyword">if</span>(ports.<span class="built_in">isEmpty</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;没有串口连接&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">const</span> QSerialPortInfo &amp;info : ports)</span><br><span class="line">        &#123;</span><br><span class="line">            my_serialPortName &lt;&lt; info.<span class="built_in">portName</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> my_serialPortName;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-2实现点击下拉框之后自动更新串口列表"><a href="#B-2实现点击下拉框之后自动更新串口列表" class="headerlink" title="B.2实现点击下拉框之后自动更新串口列表"></a>B.2实现点击下拉框之后自动更新串口列表</h3><blockquote><p>因为QT并没提供有关点击下拉框之后的信号量，所以我们这里需要重写Combobox的showPopup()函数,所以我们可以创建一个自己的Combobox类，然后在ui界面对com_combox进行提升</p></blockquote><blockquote><p>先创建一个mycombobox.cpp 和 mycombobox.h文件，并重写showPopup()函数，在这个函数中重写获取一次串口号列表，然后写到这个下拉框之中，这样就可以实现用户点击下拉框之后，就执行这些操作，注意<strong>QComboBox::showPopup();&#x2F;&#x2F;转给父类调用</strong>不要忘记，否则下拉框点击后可能不会展开</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><figcaption><span>mycombobox.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;mycombobox.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;mainwindow.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QDebug&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QComboBox&gt;</span></span></span><br><span class="line"></span><br><span class="line">mycomboBox::<span class="built_in">mycomboBox</span>(QWidget *parent) : <span class="built_in">QComboBox</span>(parent)</span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//重写下拉后的操作</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">mycomboBox::showPopup</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;qqq&quot;</span>;</span><br><span class="line"></span><br><span class="line">    QString current_text = <span class="keyword">this</span>-&gt;<span class="built_in">currentText</span>();</span><br><span class="line">    QStringList my_serialPortName;</span><br><span class="line">    my_serialPortName.<span class="built_in">clear</span>();</span><br><span class="line">    QComboBox::<span class="built_in">clear</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> ports  = QSerialPortInfo::<span class="built_in">availablePorts</span>();</span><br><span class="line">    <span class="keyword">if</span>(ports.<span class="built_in">isEmpty</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;没有串口连接&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">const</span> QSerialPortInfo &amp;info : ports)</span><br><span class="line">        &#123;</span><br><span class="line">            my_serialPortName &lt;&lt; info.<span class="built_in">portName</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    QComboBox::<span class="built_in">addItems</span>(my_serialPortName);</span><br><span class="line">    <span class="built_in">setCurrentText</span>(current_text);</span><br><span class="line"></span><br><span class="line">    QComboBox::<span class="built_in">showPopup</span>();<span class="comment">//转给父类调用</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><figcaption><span>mycombobox.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> MYCOMBOBOX_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MYCOMBOBOX_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QWidget&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QComboBox&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QSerialPort&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QSerialPortInfo&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">mycomboBox</span> : <span class="keyword">public</span> QComboBox</span><br><span class="line">&#123;</span><br><span class="line">    Q_OBJECT</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">explicit</span> <span class="title">mycomboBox</span><span class="params">(QWidget *parent = <span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">showPopup</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">signals:</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// MYCOMBOBOX_H</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-3实现”打开串口”"><a href="#B-3实现”打开串口”" class="headerlink" title="B.3实现”打开串口”"></a>B.3实现”打开串口”</h3><blockquote><p>当用户点击”打开串口”按钮后,函数中需要判断串口号，停止位，波特率等设置是否正确，然后调用下面相关函数</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//判断串口是否已打开</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">QIODevice::isOpen</span><span class="params">()</span> <span class="type">const</span>     </span></span><br><span class="line"><span class="function"><span class="comment">//清空缓冲区</span></span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">QSerialPort::clear</span><span class="params">(QSerialPort::Directions directions = AllDirections)</span> </span></span><br><span class="line"><span class="function"><span class="comment">//串口关闭</span></span></span><br><span class="line"><span class="function">[<span class="keyword">override</span> <span class="keyword">virtual</span>] <span class="type">void</span> <span class="title">QSerialPort::close</span><span class="params">()</span></span></span><br><span class="line"><span class="function"><span class="comment">//设置要打开的串口名</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">QSerialPort::setPortName</span><span class="params">(<span class="type">const</span> QString &amp;name)</span></span></span><br><span class="line"><span class="function"><span class="comment">//设置串口通信的波特率</span></span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">QSerialPort::setBaudRate</span><span class="params">(qint32 baudRate, QSerialPort::Directions directions = AllDirections)</span></span></span><br><span class="line"><span class="function"><span class="comment">//设置串口通信的数据位，数据位一般为8位</span></span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">QSerialPort::setDataBits</span><span class="params">(QSerialPort::DataBits dataBits)</span></span></span><br><span class="line"><span class="function"><span class="comment">//设置串口通信的流控制，一般无需流控制</span></span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">QSerialPort::setFlowControl</span><span class="params">(QSerialPort::FlowControl flowControl)</span></span></span><br><span class="line"><span class="function"><span class="comment">//设置串口通信的奇偶校验，一般选择“无”</span></span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">QSerialPort::setParity</span><span class="params">(QSerialPort::Parity parity)</span></span></span><br><span class="line"><span class="function"><span class="comment">//设置串口通信的停止位，停止位一般为1</span></span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">QSerialPort::setStopBits</span><span class="params">(QSerialPort::StopBits stopBits)</span></span></span><br><span class="line"><span class="function"></span></span><br></pre></td></tr></table></figure></div><blockquote><p>主函数中监听按钮是否点击，然后调用open_SerialCom(my_serialPortName);</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//监听“打开串口”按钮</span></span><br><span class="line"><span class="built_in">connect</span>(ui-&gt;open_radiobut, &amp;QRadioButton::clicked,[=]()&#123;</span><br><span class="line">    <span class="built_in">open_SerialCom</span>(my_serialPortName);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><blockquote><p>下面是对 void open_SerialCom(QStringList my_serialPortName) 的实现,其中如果遇到错误会调用err_Open_Serial()函数发出提示框给用户，并且退出函数。</p></blockquote><blockquote><p>在open_SerialCom（）函数中会判断所有下拉框，比如<strong>ui-&gt;btl_combox</strong>，<strong>ui-&gt;stop_combox</strong>等的值，需要确保下拉框设置的值与函数中使用的值一一对应，否则可能会发生错误</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//打开串口</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::open_SerialCom</span><span class="params">(QStringList my_serialPortName)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> ports  = QSerialPortInfo::<span class="built_in">availablePorts</span>();</span><br><span class="line">    <span class="keyword">if</span> (ui-&gt;open_radiobut-&gt;<span class="built_in">text</span>()==<span class="string">&quot;打开串口&quot;</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//再次检查串口是否有效</span></span><br><span class="line">        <span class="keyword">if</span>(ui-&gt;com_combox-&gt;<span class="built_in">count</span>() == <span class="number">0</span>||ports.<span class="built_in">isEmpty</span>()||my_serialPort-&gt;<span class="built_in">isOpen</span>()) <span class="comment">//当没有串口来连接</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">err_Open_Serial</span>();</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(my_serialPort-&gt;<span class="built_in">isOpen</span>())</span><br><span class="line">        &#123;</span><br><span class="line">            my_serialPort-&gt;<span class="built_in">clear</span>();</span><br><span class="line">            my_serialPort-&gt;<span class="built_in">close</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        my_serialPort-&gt;<span class="built_in">setPortName</span>(my_serialPortName[ui-&gt;com_combox-&gt;<span class="built_in">currentIndex</span>()]);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(!my_serialPort-&gt;<span class="built_in">open</span>(QIODevice::ReadWrite))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">err_Open_Serial</span>();</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//打开成功</span></span><br><span class="line">        my_serialPort-&gt;<span class="built_in">setBaudRate</span>(ui-&gt;btl_combox-&gt;<span class="built_in">currentText</span>().<span class="built_in">toInt</span>(),QSerialPort::AllDirections);<span class="comment">//设置波特率和读写方向</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">switch</span> (ui-&gt;data_combox-&gt;<span class="built_in">currentIndex</span>()) &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">0</span> :     my_serialPort-&gt;<span class="built_in">setDataBits</span>(QSerialPort::Data8); <span class="keyword">break</span>;            <span class="comment">//数据位为8位</span></span><br><span class="line">        <span class="keyword">case</span> <span class="number">1</span> :     my_serialPort-&gt;<span class="built_in">setDataBits</span>(QSerialPort::Data7); <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">2</span> :     my_serialPort-&gt;<span class="built_in">setDataBits</span>(QSerialPort::Data6); <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">3</span> :     my_serialPort-&gt;<span class="built_in">setDataBits</span>(QSerialPort::Data5); <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">default</span>: <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;数据位错误&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">switch</span> (ui-&gt;stop_combox-&gt;<span class="built_in">currentIndex</span>()) &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">0</span> :     my_serialPort-&gt;<span class="built_in">setStopBits</span>(QSerialPort::OneStop);<span class="keyword">break</span>;            <span class="comment">//一位停止位</span></span><br><span class="line">        <span class="keyword">case</span> <span class="number">1</span> :     my_serialPort-&gt;<span class="built_in">setStopBits</span>(QSerialPort::OneAndHalfStop);<span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">2</span> :     my_serialPort-&gt;<span class="built_in">setStopBits</span>(QSerialPort::TwoStop);<span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">default</span>: <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;停止位错误&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">switch</span> (ui-&gt;jiaoyan_combox-&gt;<span class="built_in">currentIndex</span>()) &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">0</span> :     my_serialPort-&gt;<span class="built_in">setParity</span>(QSerialPort::NoParity);<span class="keyword">break</span>;             <span class="comment">//无校验位</span></span><br><span class="line">        <span class="keyword">case</span> <span class="number">1</span> :     my_serialPort-&gt;<span class="built_in">setParity</span>(QSerialPort::OddParity);<span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">2</span> :     my_serialPort-&gt;<span class="built_in">setParity</span>(QSerialPort::EvenParity);<span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">default</span>: <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;校验位错误&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        my_serialPort-&gt;<span class="built_in">setFlowControl</span>(QSerialPort::NoFlowControl);   <span class="comment">//无流控制</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">connect</span>(my_serialPort,<span class="built_in">SIGNAL</span>(<span class="built_in">readyRead</span>()),<span class="keyword">this</span>,<span class="built_in">SLOT</span>(<span class="built_in">receiveInfo</span>()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//禁用部分按钮等</span></span><br><span class="line">        <span class="built_in">my_setEnabled</span>(<span class="literal">false</span>);</span><br><span class="line">        ui-&gt;open_radiobut-&gt;<span class="built_in">setText</span>(<span class="string">&quot;关闭串口&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">my_setEnabled</span>(<span class="literal">true</span>);</span><br><span class="line">        my_serialPort-&gt;<span class="built_in">close</span>();</span><br><span class="line">        ui-&gt;open_radiobut-&gt;<span class="built_in">setText</span>(<span class="string">&quot;打开串口&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//串口打开失败界面</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::err_Open_Serial</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    QMessageBox::<span class="built_in">critical</span>(<span class="keyword">this</span>, <span class="string">&quot;警告&quot;</span>, <span class="string">&quot;串口打开失败,请检查串口&quot;</span>);</span><br><span class="line">    ui-&gt;open_radiobut-&gt;<span class="built_in">setChecked</span>(<span class="literal">false</span>);</span><br><span class="line">    <span class="built_in">my_setEnabled</span>(<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-4实现接收和发送数据"><a href="#B-4实现接收和发送数据" class="headerlink" title="B.4实现接收和发送数据"></a>B.4实现接收和发送数据</h3><blockquote><p>当串口打开之后，如果接受到了数据，那么&amp;QSerialPort::readyRead会发出信号,我们需要在主函数监听这个两个信号</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//监听&quot;发送&quot;按钮</span></span><br><span class="line"><span class="built_in">connect</span>(ui-&gt;send_but,&amp;QPushButton::clicked,[=]()&#123;</span><br><span class="line">    <span class="built_in">send_data</span>();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//监听&quot;接收&quot;数据</span></span><br><span class="line"><span class="built_in">connect</span>(my_serialPort,&amp;QSerialPort::readyRead,[=]() &#123;</span><br><span class="line">    <span class="built_in">receive_data</span>();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><blockquote><p>收到的数据可以通过串口对象的my_serialPort-&gt;readAll()方法进行读取，然后我们需要判断接收的格式，进行相应的格式转换,然后将处理好的数据发送到接受数据文本框中 ui-&gt;re_text-&gt;append()</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//接收数据</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::receive_data</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    QByteArray info = my_serialPort-&gt;<span class="built_in">readAll</span>();</span><br><span class="line"></span><br><span class="line">    QString strReceiveData = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">if</span>(ui-&gt;hex_re_cheak-&gt;<span class="built_in">isChecked</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        QByteArray hexData = info.<span class="built_in">toHex</span>();</span><br><span class="line">        strReceiveData = hexData.<span class="built_in">toUpper</span>();</span><br><span class="line"></span><br><span class="line">        <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;接收到串口数据: &quot;</span>&lt;&lt;strReceiveData;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;strReceiveData.<span class="built_in">size</span>(); i+=<span class="number">2</span><span class="number">+1</span>)</span><br><span class="line">            strReceiveData.<span class="built_in">insert</span>(i, <span class="built_in">QLatin1String</span>(<span class="string">&quot; &quot;</span>));</span><br><span class="line">        strReceiveData.<span class="built_in">remove</span>(<span class="number">0</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">qDebug</span>()&lt;&lt;<span class="string">&quot;处理后的串口数据: &quot;</span>&lt;&lt;strReceiveData;</span><br><span class="line"></span><br><span class="line">        ui-&gt;re_text-&gt;<span class="built_in">append</span>(strReceiveData);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        strReceiveData = info;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (ui-&gt;gbk_re-&gt;<span class="built_in">isChecked</span>())</span><br><span class="line">        &#123;</span><br><span class="line">            QTextCodec *tc = QTextCodec::<span class="built_in">codecForName</span>(<span class="string">&quot;GBK&quot;</span>);</span><br><span class="line">            QString tmpQStr = tc-&gt;<span class="built_in">toUnicode</span>(info);</span><br><span class="line">            ui-&gt;re_text-&gt;<span class="built_in">append</span>(tmpQStr);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            QString tmpQStr = QString::<span class="built_in">fromUtf8</span>(info);</span><br><span class="line">            ui-&gt;re_text-&gt;<span class="built_in">append</span>(tmpQStr);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><blockquote><p>发送数据需要调用my_serialPort-&gt;write()函数，同样要根据按钮进行格式转换，然后再进行发送，注意在发送之前检查串口的打开情况,其中用到了两个函数，isHexString()：判断是否是hex数据，和convertStringToHex()将Qstring转换成hex数据</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//发送数据</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::send_data</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//检查串口打开情况</span></span><br><span class="line">    <span class="keyword">if</span> (!my_serialPort-&gt;<span class="built_in">isOpen</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">err_Open_Serial</span>();</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    QString my_strSendData = ui-&gt;send_text-&gt;<span class="built_in">toPlainText</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(ui-&gt;hex_send_cheak-&gt;<span class="built_in">isChecked</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        my_strSendData.<span class="built_in">remove</span>(<span class="string">&quot; &quot;</span>); <span class="comment">// 去掉空格</span></span><br><span class="line"></span><br><span class="line">        QByteArray sendBuf;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!<span class="built_in">isHexString</span>(my_strSendData))</span><br><span class="line">        &#123;</span><br><span class="line">            QMessageBox::<span class="built_in">critical</span>(<span class="keyword">this</span>, <span class="string">&quot;警告&quot;</span>, <span class="string">&quot;输入内容非16进制&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">convertStringToHex</span>(my_strSendData, sendBuf);             <span class="comment">//把QString 转换 为 hex</span></span><br><span class="line"></span><br><span class="line">        my_serialPort-&gt;<span class="built_in">write</span>(sendBuf);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(ui-&gt;gbk_send-&gt;<span class="built_in">isChecked</span>())</span><br><span class="line">        &#123;</span><br><span class="line">            my_serialPort-&gt;<span class="built_in">write</span>(my_strSendData.<span class="built_in">toLocal8Bit</span>());<span class="comment">//发送GBK编码数据</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> my_serialPort-&gt;<span class="built_in">write</span>(my_strSendData.<span class="built_in">toUtf8</span>());  <span class="comment">// 发送UTF-8编码数据</span></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    my_serialPort-&gt;<span class="built_in">write</span>(<span class="string">&quot;\r\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">MainWindow::isHexString</span><span class="params">(<span class="type">const</span> QString &amp;str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 正则表达式匹配 0-9、a-f、A-F 和空格</span></span><br><span class="line">    <span class="function">QRegularExpression <span class="title">re</span><span class="params">(<span class="string">&quot;^[0-9a-fA-F ]+$&quot;</span>)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> re.<span class="built_in">match</span>(str).<span class="built_in">hasMatch</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::convertStringToHex</span><span class="params">(<span class="type">const</span> QString &amp;str, QByteArray &amp;sendBuf)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    sendBuf.<span class="built_in">clear</span>();</span><br><span class="line">    QString hexStr = str.<span class="built_in">simplified</span>().<span class="built_in">replace</span>(<span class="string">&quot; &quot;</span>, <span class="string">&quot;&quot;</span>); <span class="comment">// 去掉空格</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; hexStr.<span class="built_in">length</span>(); i += <span class="number">2</span>) &#123;</span><br><span class="line">        <span class="type">bool</span> ok;</span><br><span class="line">        <span class="type">char</span> byte = hexStr.<span class="built_in">mid</span>(i, <span class="number">2</span>).<span class="built_in">toUShort</span>(&amp;ok, <span class="number">16</span>);</span><br><span class="line">        <span class="keyword">if</span> (ok) &#123;</span><br><span class="line">            sendBuf.<span class="built_in">append</span>(byte);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            sendBuf.<span class="built_in">clear</span>(); <span class="comment">// 如果有无效字符，清空结果</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><blockquote><p>当这几个基本功能完成之后，就可以利用vpsd(虚拟串口创建工具)，测试一下以上功能<br>先创建一对虚拟串口com1 com2 ,然后利用两个串口助手分别连接1，2，然后相互发送东西</p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_025.webp"                      width="800"                 ></blockquote><blockquote><p>测试发送接收消息</p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_026.webp"                      width="800"                 ></blockquote><blockquote><p>测试在使用其它串口助手打开串口COM1的情况下，用自己写的串口调试助手打开串口COM1</p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_027.webp"                      width="800"                 ></blockquote><h3 id="B-5实现其它基础功能及-优化"><a href="#B-5实现其它基础功能及-优化" class="headerlink" title="B.5实现其它基础功能及 优化"></a>B.5实现其它基础功能及 优化</h3><blockquote><p>下面实现“清除文本框”，以及优化当未打开串口时禁用发送按钮，打开串口后禁用部分下拉框，hex模式中禁止gbk选项，直接点击窗口关闭按钮后，主动关闭串口等，部分代码添加在打开串口和关闭串口的代码中，下面主要是函数实现</p></blockquote><blockquote><p>这是主函数中的监听函数，</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//&quot;发送&quot;默认按钮为关闭</span></span><br><span class="line">ui-&gt;send_but-&gt;<span class="built_in">setEnabled</span>(<span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//监听&quot;清空&quot;按钮</span></span><br><span class="line"><span class="built_in">connect</span>(ui-&gt;clear_re_but,&amp;QPushButton::clicked,[=]&#123;</span><br><span class="line">    ui-&gt;re_text-&gt;<span class="built_in">clear</span>();</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">connect</span>(ui-&gt;clear_send_but,&amp;QPushButton::clicked,[=]&#123;</span><br><span class="line">    ui-&gt;send_text-&gt;<span class="built_in">clear</span>();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//当hex模式时，禁止选择gbk等编码</span></span><br><span class="line"><span class="built_in">connect</span>(ui-&gt;hex_re_cheak, &amp;QPushButton::clicked, [=]() &#123;</span><br><span class="line">    <span class="built_in">toggleHexSettings</span>();</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">connect</span>(ui-&gt;hex_send_cheak, &amp;QPushButton::clicked, [=]() &#123;</span><br><span class="line">    <span class="built_in">toggleHexSettings</span>();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><blockquote><p>具体实现如下</p></blockquote><div class="code-container" data-rel="C++"><figure class="iseeu highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//打开串口后禁用部分下拉框</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::my_setEnabled</span><span class="params">(<span class="type">bool</span> flag)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ui-&gt;com_combox-&gt;<span class="built_in">setEnabled</span>(flag);</span><br><span class="line">    ui-&gt;btl_combox-&gt;<span class="built_in">setEnabled</span>(flag);</span><br><span class="line">    ui-&gt;stop_combox-&gt;<span class="built_in">setEnabled</span>(flag);</span><br><span class="line">    ui-&gt;data_combox-&gt;<span class="built_in">setEnabled</span>(flag);</span><br><span class="line">    ui-&gt;jiaoyan_combox-&gt;<span class="built_in">setEnabled</span>(flag);</span><br><span class="line">    ui-&gt;send_but-&gt;<span class="built_in">setEnabled</span>(!flag);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::toggleHexSettings</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (ui-&gt;hex_re_cheak-&gt;<span class="built_in">isChecked</span>() || ui-&gt;hex_send_cheak-&gt;<span class="built_in">isChecked</span>()) &#123;</span><br><span class="line">        <span class="comment">// 当 hex_re_cheak 或 hex_send_cheak 被勾选时，禁用相关控件</span></span><br><span class="line">        ui-&gt;gbk_re-&gt;<span class="built_in">setEnabled</span>(<span class="literal">false</span>);</span><br><span class="line">        ui-&gt;gbk_send-&gt;<span class="built_in">setEnabled</span>(<span class="literal">false</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 当都没有勾选时，恢复相关控件</span></span><br><span class="line">        ui-&gt;gbk_re-&gt;<span class="built_in">setEnabled</span>(<span class="literal">true</span>);</span><br><span class="line">        ui-&gt;gbk_send-&gt;<span class="built_in">setEnabled</span>(<span class="literal">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//直接点击窗口关闭按钮后，主动关闭串口</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MainWindow::closeEvent</span><span class="params">(QCloseEvent *event)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (my_serialPort-&gt;<span class="built_in">isOpen</span>()) &#123;</span><br><span class="line">        my_serialPort-&gt;<span class="built_in">close</span>();  <span class="comment">// 关闭串口</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    event-&gt;<span class="built_in">accept</span>();  <span class="comment">// 允许窗口关闭</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p><strong>到此一个简易的串口调试助手大功告成~</strong>,具体源码可以参考后面的工程源码，如果有问题和建议可以提出</p><h2 id="C有关软件打包的总结"><a href="#C有关软件打包的总结" class="headerlink" title="C有关软件打包的总结"></a>C有关软件打包的总结</h2><blockquote><p>如果没有exe文件图标需求：<br>当使用release编译之后，使用QT自带的命令框的指令windeployqt打包（具体步骤可以自行查找资料），然后删除不用的dll文件，减少软件体积，然后可以选择直接压缩，或则使用enigmavb软件封包，或HM NIS Edit软件创建安装向导</p></blockquote><blockquote><p>如果有exe文件需求:<br>在工程文件源代码中添加一个图片文件（注意不是添加资源文件），要求必须为.ico格式（可以在网上在线转换格式，不要直接改后缀名）<br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_028.webp"                      width="800"                 ><br>然后在工程的.pro文件中最后添加一句：<strong>RC_ICONS &#x3D; mouse.ico</strong>  注意，后面是自己的图标文件名字，最后再进行编译运行，可以发现自己的debug文件中的exe文件的图标已经改成你的图标了<br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_030.webp"                      width="800"                 ><br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Qt_1\word_00001_029.webp"                      width="800"                 ><br>然后同第一个步骤一样就可以打包成自己的exe可执行文件了！</p></blockquote><h2 id="D结尾"><a href="#D结尾" class="headerlink" title="D结尾"></a>D结尾</h2><p>这只是一个QT例子，其中有很多不足，可能无法在实际调试(比如stm32)中使用，仅供学习参考emm</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><a class="link"   href="https://doc.qt.io/" >QT官网文档<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="文件地址"><a href="#文件地址" class="headerlink" title="文件地址"></a>文件地址</h3><blockquote><ol><li><a class="link"   href="https://github.com/Hunhaozi/Qt_Serial_-" >github仓库<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link"   href="mailto:&#51;&#x31;&#52;&#54;&#55;&#48;&#x32;&#51;&#x36;&#50;&#64;&#113;&#113;&#x2e;&#x63;&#x6f;&#x6d;" >邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">学完Qt之后，突然就想写一个自己的串口调试助手，顺便巩固一下刚刚学的知识吧</summary>
    
    
    
    <category term="Qt" scheme="https://blog.haozi-haozi.cn/categories/Qt/"/>
    
    
    <category term="Qt" scheme="https://blog.haozi-haozi.cn/tags/Qt/"/>
    
    <category term="Serial" scheme="https://blog.haozi-haozi.cn/tags/Serial/"/>
    
  </entry>
  
  <entry>
    <title>STM32——MCU/传感器/模块相关——学习笔记</title>
    <link href="https://blog.haozi-haozi.cn/2024/11/01/embedded_mcu/"/>
    <id>https://blog.haozi-haozi.cn/2024/11/01/embedded_mcu/</id>
    <published>2024-11-01T04:11:21.000Z</published>
    <updated>2026-03-24T08:58:13.967Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-前情提要"><a href="#A-前情提要" class="headerlink" title="A. 前情提要"></a>A. 前情提要</h2><p>隔~ 补充巩固一下基础知识</p><h2 id="B-STM32"><a href="#B-STM32" class="headerlink" title="B. STM32"></a>B. STM32</h2><h3 id="B-1-启动流程"><a href="#B-1-启动流程" class="headerlink" title="B.1 启动流程"></a>B.1 启动流程</h3><h4 id="B1-1-MAP文件"><a href="#B1-1-MAP文件" class="headerlink" title="B1.1 MAP文件"></a>B1.1 MAP文件</h4><p>在了解启动流程之前，先了解一下OUPUT文件里面有什么，程序通过MDK编译后会产生很多文件，<strong>比如.o .axf .hex ,.map等文件</strong>，这里主要介绍这四种:</p><ul><li><p><strong>.o</strong> : <strong>可重定向对象文件</strong>，每个.c&#x2F;.s文件编译后都会对应一个.o文件</p></li><li><p><strong>.axf</strong>: <strong>可执行对象文件</strong>,由很多.o文件链接生成，仿真的时候需要用到</p></li><li><p><strong>.hex</strong>:INTL HEX格式文件，<strong>用于下载到MCU运行</strong>，由.axf转化而来</p></li><li><p><strong>.map</strong>：<strong>连接器生成的列表文件</strong>，用来分析程序存储占用情况非常有用</p></li></ul><p>MAP文件中包括了各种 .c文件，函数，符号等的地址，大小，引用等信息，可以用来分析c文件占用FLASH和RAM的大小。整个文件主要由五部分组成:</p><ol><li>程序段交叉引用关系</li><li>删除映像未使用的程序段</li><li>映像符号表</li><li>映像内存分布图</li><li>映像组件大小</li></ol><p>下面是map文件的局部内容:</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line">Component: ARM Compiler 5.05 update 1 (build 106) Tool: armlink [4d0efa]</span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br><span class="line">Section Cross References</span><br><span class="line"></span><br><span class="line">    main.o(i.main) refers to delay.o(i.delay_init) for delay_init</span><br><span class="line">    main.o(i.main) refers to led.o(i.LED_Init) for LED_Init</span><br><span class="line">    main.o(i.main) refers to delay.o(i.delay_ms) for delay_ms</span><br><span class="line"></span><br><span class="line">    /*.......*/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br><span class="line">Removing Unused input sections from the image.</span><br><span class="line"></span><br><span class="line">    Removing system_stm32f10x.o(i.SystemCoreClockUpdate), (164 bytes).</span><br><span class="line">    Removing delay.o(i.delay_us), (76 bytes).</span><br><span class="line">    Removing sys.o(.emb_text), (6 bytes).</span><br><span class="line"></span><br><span class="line">    /*.......*/</span><br><span class="line"></span><br><span class="line">91 unused section(s) (total 3322 bytes) removed from the image.</span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br><span class="line">Image Symbol Table</span><br><span class="line"></span><br><span class="line">    Local Symbols   //局部符号</span><br><span class="line"></span><br><span class="line">    Symbol Name                              Value     Ov Type        Size  Object(Section)</span><br><span class="line"></span><br><span class="line">    ../clib/angel/boardlib.s                 0x00000000   Number         0  boardinit1.o ABSOLUTE</span><br><span class="line">    ../clib/angel/boardlib.s                 0x00000000   Number         0  boardinit3.o ABSOLUTE</span><br><span class="line"></span><br><span class="line">        /*.......*/</span><br><span class="line"></span><br><span class="line">    Global Symbols    //全局符号</span><br><span class="line"></span><br><span class="line">    Symbol Name                              Value     Ov Type        Size  Object(Section)</span><br><span class="line"></span><br><span class="line">    BuildAttributes$$THM_ISAv4$P$D$K$B$S$PE$A:L22UL41UL21$X:L11$S22US41US21$IEEE1$IW$USESV6$~STKCKD$USESV7$~SHL$OSPACE$ROPI$EBA8$UX$STANDARDLIB$REQ8$PRES8$EABIv2 0x00000000   Number         0  anon$$obj.o ABSOLUTE</span><br><span class="line">    __ARM_use_no_argv                        0x00000000   Number         0  main.o ABSOLUTE</span><br><span class="line"></span><br><span class="line">        /*.......*/</span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br><span class="line">Memory Map of the image</span><br><span class="line"></span><br><span class="line">  Image Entry point : 0x080001cd</span><br><span class="line"></span><br><span class="line">  Load Region LR_1 (Base: 0x08000000, Size: 0x00000784, Max: 0xffffffff, ABSOLUTE)</span><br><span class="line"></span><br><span class="line">    Execution Region ER_RO (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x00000764, Max: 0xffffffff, ABSOLUTE)</span><br><span class="line"></span><br><span class="line">    Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object</span><br><span class="line"></span><br><span class="line">        /*.......*/</span><br><span class="line"></span><br><span class="line">    0x08000398   0x08000398   0x0000004c   Code   RO          241    i.LED_Init          led.o</span><br><span class="line"></span><br><span class="line">        /*.......*/</span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br><span class="line">Image component sizes</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name</span><br><span class="line"></span><br><span class="line">       144         22          0          4          0       1664   delay.o</span><br><span class="line">        76          8          0          0          0        659   led.o</span><br><span class="line">        64          8          0          0          0     223083   main.o</span><br><span class="line"></span><br><span class="line">                /*.......*/</span><br><span class="line"></span><br><span class="line">    ----------------------------------------------------------------------</span><br><span class="line">      1296        112        336         32       1736     245427   Object Totals</span><br><span class="line">         0          0         32          0          0          0   (incl. Generated)</span><br><span class="line">         6          0          0          2          0          0   (incl. Padding)</span><br><span class="line"></span><br><span class="line">    ----------------------------------------------------------------------</span><br><span class="line"></span><br><span class="line">      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Library Member Name</span><br><span class="line"></span><br><span class="line">         8          0          0          0          0         68   __main.o</span><br><span class="line">         0          0          0          0          0          0   __rtentry.o</span><br><span class="line"></span><br><span class="line">                 /*.......*/</span><br><span class="line"></span><br><span class="line">    ----------------------------------------------------------------------</span><br><span class="line">       260         12          0          0         96        576   Library Totals</span><br><span class="line">         4          0          0          0          0          0   (incl. Padding)</span><br><span class="line"></span><br><span class="line">    ----------------------------------------------------------------------</span><br><span class="line"></span><br><span class="line">      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Library Name</span><br><span class="line"></span><br><span class="line">       256         12          0          0         96        576   c_w.l</span><br><span class="line"></span><br><span class="line">    ----------------------------------------------------------------------</span><br><span class="line">       260         12          0          0         96        576   Library Totals</span><br><span class="line"></span><br><span class="line">    ----------------------------------------------------------------------</span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   </span><br><span class="line"></span><br><span class="line">      1556        124        336         32       1832     244855   Grand Totals</span><br><span class="line">      1556        124        336         32       1832     244855   ELF Image Totals</span><br><span class="line">      1556        124        336         32          0          0   ROM Totals</span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br><span class="line">    Total RO  Size (Code + RO Data)                 1892 (   1.85kB)</span><br><span class="line">    Total RW  Size (RW Data + ZI Data)              1864 (   1.82kB)</span><br><span class="line">    Total ROM Size (Code + RO Data + RW Data)       1924 (   1.88kB)</span><br><span class="line"></span><br><span class="line">==============================================================================</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><hr><h4 id="B1-2-启动模式"><a href="#B1-2-启动模式" class="headerlink" title="B1.2 启动模式"></a>B1.2 启动模式</h4><p>M3&#x2F;M4&#x2F;M7内核按下复位键之后:</p><ol><li>从地址 0x0000 0000 取出堆栈指针 MSP 的初始值，该值就是栈顶地址</li><li>从地址 0x0000 0004 取出 程序计数器指针 PC 的值，该值是复位向量</li></ol><p>不同厂商会把 这两个地址映射到不同的位置，比如对于stm32F1,在系统复位后，<strong>STSCLK的第四个上升沿，BOOT引脚的值会被锁存</strong></p><table><thead><tr><th>BOOT1</th><th>BOOT0</th><th>启动模式</th><th>0x00000000 映射地址</th><th>0x00000004 映射地址</th></tr></thead><tbody><tr><td>x</td><td>0</td><td>主闪存（Main Flash）</td><td>0x08000000</td><td>0x08000004</td></tr><tr><td>0</td><td>1</td><td>系统存储器（System Memory &#x2F; Bootloader）</td><td>0x1FFFF000</td><td>0x1FFFF004</td></tr><tr><td>1</td><td>1</td><td>内置 SRAM（SRAM）</td><td>0x20000000</td><td>0x20000004</td></tr></tbody></table><p>第一种最常用，第二种是bootloader下载使用，第三种不常用，这里也主要介绍F1，其它系列大家可以网上查询</p><h4 id="B1-3-启动过程-内部Flash启动为例"><a href="#B1-3-启动过程-内部Flash启动为例" class="headerlink" title="B1.3 启动过程 (内部Flash启动为例)"></a>B1.3 启动过程 (内部Flash启动为例)</h4><p><strong>Reset</strong> -&gt; <strong>获取MSP值</strong>(0x08000000)-&gt;<strong>获取PC值</strong>(0x08000004)-&gt;<strong>Reset_Handler（复位中断函数）</strong>-&gt;<strong>启动文件.s</strong>-&gt;<strong>main函数</strong></p><p>所以下面主要介绍启动文件.S</p><h4 id="B1-3-启动文件"><a href="#B1-3-启动文件" class="headerlink" title="B1.3 启动文件"></a>B1.3 启动文件</h4><p><strong>初始化MSP</strong>-&gt;<strong>初始化PC</strong>-&gt;<strong>设置堆栈大小</strong>-&gt;<strong>初始化中断向量表</strong>(__Vercors)-&gt;<strong>调用初始化函数</strong>(比如Systeminit（可选）)-&gt;<strong>调用__main</strong>(标准C库，执行一系列设置后调用main函数)</p><pre class="mermaid">flowchart TD  StartAsm["startup.s 开始 (汇编入口)"]  Init_MSP["设置初始堆栈指针 MSP\n(来自 Vector[0])"]  Option_Vec["(可选) 设置或拷贝向量表到固定地址"]  DisableIRQ["(可选) 禁用中断"]  CopyData["拷贝 .data 段到 RAM"]  ClearBSS["清零 .bss 段"]  CallSystemInit["(可选) 调用 SystemInit() 配置时钟等"]  Call___main["调用运行时起始函数（如 __main/_start）"]  JumpMain["跳转到 main()"]  StartAsm --> Init_MSP  Init_MSP --> Option_Vec  Option_Vec --> DisableIRQ  DisableIRQ --> CopyData  CopyData --> ClearBSS  ClearBSS --> InitCtors  InitCtors --> CallSystemInit  CallSystemInit --> Call___main  Call___main --> JumpMain</pre><h3 id="B-2-低功耗"><a href="#B-2-低功耗" class="headerlink" title="B.2 低功耗"></a>B.2 低功耗</h3><h4 id="B-2-1-电源分布"><a href="#B-2-1-电源分布" class="headerlink" title="B.2.1 电源分布"></a>B.2.1 电源分布</h4><p>stm32f1的电源主要由3.3V供电，分为<strong>模拟供电区</strong>(VDDA)，<strong>数字供电区</strong>(VDD)，还有<strong>存储用的区域(这里通常为1.8V)</strong>,最后还有一个<strong>VBAT的供电区</strong>(主要给RTC使用)</p><h4 id="B-2-2-低功耗模式"><a href="#B-2-2-低功耗模式" class="headerlink" title="B.2.2 低功耗模式"></a>B.2.2 低功耗模式</h4><p>主要有四种模式: <strong>运行模式，睡眠模式，停止模式，待机模式</strong>，通常一上电默认的是运行模式</p><p>对于stm32整体可以理解成 <strong>电压调节器+时钟系统 分别对外设（SPI，IIC等），CM3内核，存储器三部分输入</strong></p><ul><li><strong>运行模式</strong></li></ul><p>所有外设都正常工作<br>供应电流(典型值):<em><strong>51mA</strong></em></p><ul><li><strong>睡眠模式</strong> ：<strong>时钟系统停止对内核的输入</strong>，但其它都正常，即外设可以正常使用(比如DMA)</li></ul><p>优点: <strong>对系统影响小</strong><br>缺点: <strong>节能效果最差</strong><br>唤醒: <strong>如果立即休眠，使用任意中断唤醒</strong><br>唤醒时间: <strong>1.8us</strong><br>供应电流(典型值): <em><strong>29.5mA</strong></em></p><ul><li><strong>停止模式</strong> ： <strong>时钟系统直接关闭</strong>，但是电压调节器开着，即存储器还有内容，程序不会复位</li></ul><p>优点: <strong>节能效果好，且程序不会复位</strong><br>缺点：<strong>恢复时间长，时钟系统需要重新启振</strong><br>唤醒: <strong>使用任意外部中断唤醒</strong>，唤醒后的时钟会变成HSI RC震荡器(8MHz),所以需要唤醒后重新设置时钟<br>唤醒时间: <strong>5.4us</strong><br>供应电流(典型值): <strong>35uA</strong></p><ul><li><strong>待机模式</strong> : <strong>关闭时钟系统和电压调节器</strong></li></ul><p>优点: <strong>节能效果最好</strong><br>缺点：<strong>程序会复位，少数条件唤醒</strong><br>唤醒: <strong>WKUP引脚的上升沿，RTC闹钟(事件)，NRST引脚的外部复位，IWDG复位</strong><br>唤醒时间: <strong>50us</strong><br>供应电流(典型值): <em><strong>3.8uA</strong></em></p><h4 id="B-2-3-例程代码"><a href="#B-2-3-例程代码" class="headerlink" title="B.2.3 例程代码"></a>B.2.3 例程代码</h4><p>下面主要给出三种，模式如何进入，以及相关注意特征</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 进入 Sleep（CPU 低功耗等待中断） */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Enter_Sleep_Mode</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* 可在此关闭/降频外设以节省更多功耗 */</span></span><br><span class="line">    <span class="comment">/* 使用 CMSIS 或 StdPeriph: __WFI() */</span></span><br><span class="line">    __WFI();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进入 Stop：使用标准库 PWR_EnterSTOPMode（StdPeriph） */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Enter_Stop_Mode</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* 使能 PWR 外设时钟（确保已经使能） */</span></span><br><span class="line">    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 如果使用低功耗寄存器需要设置 */</span></span><br><span class="line">    <span class="comment">/* 进入 STOP (内部稳压器保持) 并使用 WFI 进入 */</span></span><br><span class="line">    PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 代码在此处会被唤醒后继续执行（唤醒后时钟需重建） */</span></span><br><span class="line">    <span class="comment">//这里可以重新初始化时钟</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 进入 Standby：StdPeriph 提供 PWR_EnterSTANDBYMode() */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Enter_Standby_Mode</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* 清 WU 标志，确保下一次能唤醒 */</span></span><br><span class="line">    PWR_ClearFlag(PWR_FLAG_WU);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 进入待机（不会返回，唤醒通常触发复位） */</span></span><br><span class="line">    PWR_EnterSTANDBYMode();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//这里的代码不会执行，会直接复位</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><hr><h2 id="C-传感器常用"><a href="#C-传感器常用" class="headerlink" title="C 传感器常用"></a>C 传感器常用</h2><p>这里主要根据我的具体项目&#x2F;比赛来介绍:</p><hr><h3 id="C-1-电赛相关-2025-省"><a href="#C-1-电赛相关-2025-省" class="headerlink" title="C.1 电赛相关(2025-省)"></a>C.1 电赛相关(2025-省)</h3><ul><li><strong>常见七路循迹模块</strong></li></ul><p>协议: <strong>GPIO</strong> 直接读取</p><p>探测距离：<strong>10~50mm</strong></p><p>使用方式： 通过<strong>调节模块上的电位器更改基准电压</strong>，通过电压比较器判断基准电压和检测到的电压(接收管，根据光照灰度等)，然后输出固定的数字量IO值，通过stm32读取之后作为循迹算法的输入</p><ul><li><strong>九轴陀螺仪 JY901</strong></li></ul><p>协议: <strong>串口&#x2F;IIC</strong>,通常使用串口</p><p>精度: 静态情况: <strong>0.05°(x,y) 1°(z)</strong>  动态情况: <strong>0.1°(x,y)  1°(z)</strong></p><p>回传速率: <strong>0.2~200Hz回传速率</strong>，需要<strong>通过上位机来配置</strong>陀螺仪的回传速率，以及设置回传什么东西</p><p>速率: <strong>串口–最小 4800bps  最大230400bps（默认9600bps）</strong> <strong>IIC</strong>分两种情况，<strong>软件(100K)硬件(400k)</strong></p><ul><li><p><strong>K230</strong></p></li><li><p><strong>520电机</strong></p></li><li><p><strong>步进电机</strong></p></li><li><p><strong>舵机</strong></p></li></ul><h3 id="C-2-电赛备赛相关"><a href="#C-2-电赛备赛相关" class="headerlink" title="C.2 电赛备赛相关"></a>C.2 电赛备赛相关</h3><ul><li><p><strong>激光测距模块</strong></p></li><li><p><strong>openmv</strong></p></li><li><p><strong>角度传感器(不是陀螺仪)</strong></p></li></ul><h3 id="C-3-无人机"><a href="#C-3-无人机" class="headerlink" title="C.3 无人机"></a>C.3 无人机</h3><ul><li><p><strong>陀螺仪同上</strong></p></li><li><p><strong>气压计</strong></p></li><li><p><strong>光流传感器</strong></p></li></ul><h3 id="C-4-IMX6ULL相关"><a href="#C-4-IMX6ULL相关" class="headerlink" title="C.4 IMX6ULL相关"></a>C.4 IMX6ULL相关</h3><ul><li><p><strong>陀螺仪同上</strong></p></li><li><p><strong>气压计</strong></p></li><li><p><strong>光流传感器</strong></p></li><li><p><strong>温湿度传感器</strong></p></li><li><p><strong>GPS模块</strong></p></li></ul><blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link"   href="mailto:&#51;&#49;&#x34;&#54;&#x37;&#48;&#50;&#51;&#54;&#x32;&#x40;&#113;&#x71;&#46;&#99;&#111;&#109;" >邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">这里主要记录有关mcu，主要以stm32为主的一些知识，用来巩固，同时介绍一些常用的传感器/模块，记录大致参数和接口协议</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="STM32" scheme="https://blog.haozi-haozi.cn/tags/STM32/"/>
    
    <category term="MCU" scheme="https://blog.haozi-haozi.cn/tags/MCU/"/>
    
  </entry>
  
  <entry>
    <title>嵌入式——FreeRTOS操作系统——学习笔记</title>
    <link href="https://blog.haozi-haozi.cn/2024/10/29/embedded_freertos/"/>
    <id>https://blog.haozi-haozi.cn/2024/10/29/embedded_freertos/</id>
    <published>2024-10-29T00:07:49.000Z</published>
    <updated>2026-03-24T08:57:41.527Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A引入"><a href="#A引入" class="headerlink" title="A引入"></a>A引入</h2><h3 id="A-1移植FreeRTOS至Stm32F103C8T6（标准库）"><a href="#A-1移植FreeRTOS至Stm32F103C8T6（标准库）" class="headerlink" title="A.1移植FreeRTOS至Stm32F103C8T6（标准库）"></a>A.1移植FreeRTOS至Stm32F103C8T6（标准库）</h3><p>因为暂时只学了标准库，而正点原子里的教程移植HAL库，所以参考别人<a class="link"   href="https://blog.csdn.net/cairongshou/article/details/129090567%E6%9D%A5%E7%A7%BB%E6%A4%8D%E3%80%82" >https://blog.csdn.net/cairongshou/article/details/129090567来移植。<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a><br>虽然最后发现移植过程都差不太多emm</p><h2 id="B笔记"><a href="#B笔记" class="headerlink" title="B笔记"></a>B笔记</h2><p>先附一个图<br><img                       lazyload                     src="/images/loading.svg"                     data-src="/img/blog_word/word_a00007.webp"                      width="800"                 ></p><h3 id="B-1临界段-区代码保护"><a href="#B-1临界段-区代码保护" class="headerlink" title="B.1临界段&#x2F;区代码保护"></a>B.1临界段&#x2F;区代码保护</h3><blockquote><p>什么是临界段：  </p></blockquote><p>临界段代码也称临界区，指那些必须完整运行而不能被中断打断的代码段。</p><blockquote><p>适用场合:</p></blockquote><p>1：外设的各种初始化函数，IIC,SPI等等<br>2：系统自身需求<br>3：用户需求</p><blockquote><p>什么东西可以打断当前程序的运行</p></blockquote><ol><li>中断</li><li>任务调度 PendSV<br><code>PendSV是可悬起异常，如果我们把它配置最低优先级，那么如果同时有多个异常被触发，它会在其他异常执行完毕后再执行，而且任何异常都可以中断它。由于PendSV的特点就是支持【缓期执行】，所以嵌入式OS可以利用它这个特点，进行任务调度过程的上下文切换。</code></li></ol><blockquote><p>怎么进入临界区</p></blockquote><p><strong>调用API函数：</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">taskENTER_CRITICAL();           <span class="comment">//任务级进入临界区  </span></span><br><span class="line"><span class="comment">/*本质上就是关闭中断，但是FreeRTOS只能管理5~15优先级之间的中断,也就是说进入临界区之后优先级大的那几个中断(0~4)</span></span><br><span class="line"><span class="comment">  还是可以打断程序执行 */</span></span><br><span class="line">taskEXIT_CRITICAL();              <span class="comment">//任务级退出临界区  </span></span><br><span class="line"><span class="comment">//本质上就是打开中断</span></span><br><span class="line">taskENTER_CRITICAL_FROM_ISR();  <span class="comment">//中断级进入临界区</span></span><br><span class="line">taskEXIT_CRITICAL_FROM_ISR();   <span class="comment">//中断级退出临界区</span></span><br></pre></td></tr></table></figure></div><p><strong>任务级示例：</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">taskENTER_CRITICAL();  <span class="comment">//进入</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="comment">/*临界区代码*/</span></span><br><span class="line">&#125;</span><br><span class="line">taskEXIT_CRITICAL();   <span class="comment">//退出</span></span><br></pre></td></tr></table></figure></div><p><strong>中断级示例：</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">ISR</span><span class="params">()</span>  <span class="comment">/*中断服务函数*/</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">uint32_t</span> save_status; <span class="comment">//用来记录中断屏蔽寄存器的值</span></span><br><span class="line">  save_status = taskENTER_CRITICAL_FROM_ISR();  <span class="comment">//进入</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="comment">/*临界区代码*/</span></span><br><span class="line">  &#125;</span><br><span class="line">  taskEXIT_CRITICAL_FROM_ISR(save_status);    <span class="comment">//退出</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><blockquote><p>任务调度器的挂起</p></blockquote><p>挂起任务调度器，但是不关闭中断，仅仅是为了防止任务与任务之间的资源争夺。<br>适合用于临界区在任务与任务之间，不用区延时中断，又可以保证临时区的安全。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">xTaskSuspendALL(); <span class="comment">//挂起任务调度器</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="comment">/*临界区代码*/</span></span><br><span class="line">&#125;</span><br><span class="line">xTaskResumeALL(); <span class="comment">//恢复任务调度器</span></span><br></pre></td></tr></table></figure></div><h3 id="B-2列表和列表项"><a href="#B-2列表和列表项" class="headerlink" title="B.2列表和列表项"></a>B.2列表和列表项</h3><p>列表是 FreeRTOS 中的一个数据结构，概念上和链表有点类似，列表被用来跟踪 FreeRTOS中的任务。<br>列表项就是存放在列表中的项目。<br>(长的就是个双向循环链表，列表项相当于节点)<br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Embedded_1\word_00001_018.webp"                      width="800"                 ><br><code>优点同链表，成员数量好更改，增删改查</code><br><strong>列表和列表项的定义：</strong></p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">xLIST</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    listFIRST_LIST_INTEGRITY_CHECK_VALUE      <span class="comment">/*校验值（调试中使用，一般默认不开启）*/</span></span><br><span class="line">    <span class="keyword">volatile</span> UBaseType_t uxNumberOfItems;      <span class="comment">/*列表中的列表项数量（不包含末尾/迷你列表项）*/</span></span><br><span class="line">    ListItem_t * configLIST_VOLATILE pxIndex; <span class="comment">/*用于遍历列表项的指针*/</span></span><br><span class="line">    MiniListItem_t xListEnd;                  <span class="comment">/*末尾列表项/迷你列表项*/</span></span><br><span class="line">    listSECOND_LIST_INTEGRITY_CHECK_VALUE     <span class="comment">/*校验值*/</span></span><br><span class="line">&#125; List_t;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">xLIST_ITEM</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE            <span class="comment">/*检测列表项完整性*/</span></span><br><span class="line">    configLIST_VOLATILE TickType_t xItemValue;           <span class="comment">/*列表项的值*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">xLIST_ITEM</span> * <span class="title">configLIST_VOLATILE</span> <span class="title">pxNext</span>;</span>      <span class="comment">/*下一个列表项*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">xLIST_ITEM</span> * <span class="title">configLIST_VOLATILE</span> <span class="title">pxPrevious</span>;</span>  <span class="comment">/*上一个列表项*/</span></span><br><span class="line">    <span class="type">void</span> * pvOwner;                                      <span class="comment">/*列表项的拥有者(比如Task1)*/</span>    </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">xLIST</span> * <span class="title">configLIST_VOLATILE</span> <span class="title">pxContainer</span>;</span>      <span class="comment">/*列表项所在列表(比如就绪态列表)*/</span></span><br><span class="line">    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE           <span class="comment">/*检测列表项完整性*/</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">xLIST_ITEM</span> <span class="title">ListItem_t</span>;</span> </span><br></pre></td></tr></table></figure></div><p><strong>迷你列表项&#x2F;末尾列表项</strong><br>迷你列表项也是列表项，但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项，类似于链表的头结点<br>迷你列表项中没有成员变量，以节省开销</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">xMINI_LIST_ITEM</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE              <span class="comment">/*检测列表项完整性*/</span></span><br><span class="line">    configLIST_VOLATILE TickType_t xItemValue;             <span class="comment">/*列表项的值*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">xLIST_ITEM</span> * <span class="title">configLIST_VOLATILE</span> <span class="title">pxNext</span>;</span>        <span class="comment">/*上一个列表*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">xLIST_ITEM</span> * <span class="title">configLIST_VOLATILE</span> <span class="title">pxPrevious</span>;</span>    <span class="comment">/*下一个列表*/</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">xMINI_LIST_ITEM</span> <span class="title">MiniListItem_t</span>;</span></span><br></pre></td></tr></table></figure></div><p><code>列表项的初始化和插入等链表的插入基本相似</code><br>列表相关的API函数代码详解查看手册《FreeRTOS开发指南》第七章 –“FreeRTos列表和列表项’</p><h3 id="B-2任务调度器"><a href="#B-2任务调度器" class="headerlink" title="B.2任务调度器"></a>B.2任务调度器</h3><blockquote><p>API函数</p></blockquote><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">vTaskStartScheduler();  <span class="comment">//开启任务调度器</span></span><br><span class="line"><span class="comment">//....全是源码，后面再看吧</span></span><br></pre></td></tr></table></figure></div><h3 id="B-3时间片调度"><a href="#B-3时间片调度" class="headerlink" title="B.3时间片调度"></a>B.3时间片调度</h3><blockquote><p>简介</p></blockquote><p>在FreeRTOS中，一个时间片等于 SysTick的中断周期，可以通过<strong>设置滴答定时器的中断频率</strong>来更改时间片</p><blockquote><p>运行过程</p></blockquote><p>在相同优先级的n个任务，第一个任务运行一个时间片后，切换到第二个任务，第二个任务运行一个时间片后，切换到第三个任务……直到到最后一个任务，碰到阻塞，再运行第一个任务，不断循环。</p><h3 id="B-4各种任务相关API函数"><a href="#B-4各种任务相关API函数" class="headerlink" title="B.4各种任务相关API函数"></a>B.4各种任务相关API函数</h3><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Embedded_1\word_00001_019.webp"                      width="800"                 ><p>在使用 uxTaskGetSystemState() 函数的时候出现了问题</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//任务二：实现任务API函数使用</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task2</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> </span><br><span class="line"> UBaseType_t priority_num = <span class="number">0</span>;</span><br><span class="line"> priority_num = uxTaskPriorityGet(task1_handler);  <span class="comment">//获取任务1的任务优先级</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;Task1任务优先级:%d\r\n&quot;</span>,priority_num);</span><br><span class="line">    </span><br><span class="line"> vTaskPrioritySet(task2_handler,<span class="number">23</span>);       <span class="comment">//设置任务2的优先级为23</span></span><br><span class="line"> priority_num = uxTaskPriorityGet(<span class="literal">NULL</span>);   <span class="comment">//获取任务的2任务优先级</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;Task2任务优先级:%d\r\n&quot;</span>,priority_num);</span><br><span class="line"></span><br><span class="line"> UBaseType_t task_num = <span class="number">0</span>;</span><br><span class="line"> task_num = uxTaskGetNumberOfTasks(); <span class="comment">//获取任务总数 </span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务总数:%d\r\n&quot;</span>,task_num);</span><br><span class="line"> <span class="comment">//启动任务调度器函数中开启了两个任务: 空闲任务 和 软件定时器任务 加上这个文件创建的3个任务一共是5个</span></span><br><span class="line"> <span class="comment">//虽然已经删除启动任务了，但是只有在执行空闲函数的时候，才会真正删除启动任务吧（应该）</span></span><br><span class="line"> </span><br><span class="line"> UBaseType_t task_num2;</span><br><span class="line"> TaskStatus_t * status_array;</span><br><span class="line"> status_array = pvPortMalloc( task_num * <span class="keyword">sizeof</span>( TaskStatus_t ) );   <span class="comment">//申请内存空间：总任务数*结构体大小</span></span><br><span class="line"> <span class="comment">//pvPortMalloc() 为FreeRTOS提供的函数</span></span><br><span class="line"> <span class="comment">//status_array = (TaskStatus_t *)my_mem_malloc(SRAMIN,(sizeof(TaskStatus_t) * task_num));  //正点原子内存管理函数</span></span><br><span class="line"> <span class="comment">//status_array = (TaskStatus_t *)malloc((sizeof(TaskStatus_t) * task_num)); //c语言库函数</span></span><br><span class="line"> </span><br><span class="line"> task_num2 = uxTaskGetSystemState(status_array,task_num,<span class="literal">NULL</span>);    <span class="comment">//查找所有任务的信息  返回值是任务个数</span></span><br><span class="line">                                    <span class="comment">/* TaskStatus_t * const pxTaskStatusArray              */</span></span><br><span class="line">                                    <span class="comment">/* const UBaseType_t uxArraySize                       */</span></span><br><span class="line">                                    <span class="comment">/* configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime */</span> <span class="comment">//时间统计，这里先不使用</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务名\t\t任务优先级\t任务编号\r\n&quot;</span>);</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;task_num2;i++)     <span class="comment">//循环打印</span></span><br><span class="line"> &#123;</span><br><span class="line">  Serial_Printf(<span class="string">&quot;%s\t\t%d\t%d\r\n&quot;</span>,status_array[i].pcTaskName,</span><br><span class="line">           status_array[i].uxCurrentPriority,</span><br><span class="line">           status_array[i].xTaskNumber);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//这里我自己的打印结果   里面并没有 task1 任务的信息</span></span><br><span class="line"> <span class="comment">//                     或者 是 task1 任务的信息有误（任务编号无穷大/任务名乱码）</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//下面为当没有task1任务的情况出现时我加的代码</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;1:%s\r\n&quot;</span>,status_array[<span class="number">0</span>].pcTaskName);</span><br><span class="line"> Serial_Printf(<span class="string">&quot;2:%s\r\n&quot;</span>,status_array[<span class="number">1</span>].pcTaskName);  <span class="comment">//发现这个数组里面直接为空</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;3:%s\r\n&quot;</span>,status_array[<span class="number">2</span>].pcTaskName);</span><br><span class="line"> Serial_Printf(<span class="string">&quot;4:%s\r\n&quot;</span>,status_array[<span class="number">3</span>].pcTaskName);</span><br><span class="line"> Serial_Printf(<span class="string">&quot;5:%s\r\n&quot;</span>,status_array[<span class="number">4</span>].pcTaskName);</span><br><span class="line"> Serial_Printf(<span class="string">&quot;6:%s\r\n&quot;</span>,status_array[<span class="number">5</span>].pcTaskName);</span><br><span class="line"> </span><br><span class="line"> TaskStatus_t * status_array2;</span><br><span class="line"> status_array2 = pvPortMalloc( <span class="keyword">sizeof</span>( TaskStatus_t ) );   <span class="comment">//申请内存空间</span></span><br><span class="line"> </span><br><span class="line">  vTaskGetInfo( task1_handler,status_array2,pdTRUE,eInvalid);</span><br><span class="line">            <span class="comment">/*  TaskHandle_t xTask,</span></span><br><span class="line"><span class="comment">                TaskStatus_t * pxTaskStatus,</span></span><br><span class="line"><span class="comment">                BaseType_t xGetFreeStackSpace,</span></span><br><span class="line"><span class="comment">                eTaskState eState )             */</span></span><br><span class="line"> </span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务名：%s\r\n&quot;</span>,status_array2-&gt;pcTaskName);               <span class="comment">//这里依旧有问题</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务优先级：%ld\r\n&quot;</span>,status_array2-&gt;uxCurrentPriority);</span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务编号：%ld\r\n&quot;</span>,status_array2-&gt;xTaskNumber);           <span class="comment">//这里依旧有问题</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务状态：%d\r\n&quot;</span>,status_array2-&gt;eCurrentState);</span><br><span class="line"> </span><br><span class="line"> TaskHandle_t task1_handle = <span class="number">0</span>;</span><br><span class="line"> task1_handle = xTaskGetHandle(<span class="string">&quot;Task1&quot;</span>);    <span class="comment">//通过任务名获取任务句柄</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务1句柄：%#x\r\n&quot;</span>,(<span class="type">int</span>)task1_handle);    <span class="comment">//这里输出的是0，还是获取失败</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务1句柄：%#x\r\n&quot;</span>,(<span class="type">int</span>)task1_handler);</span><br><span class="line"></span><br><span class="line"> TaskHandle_t task2_handle = <span class="number">0</span>;</span><br><span class="line"> task2_handle = xTaskGetHandle(<span class="string">&quot;Task2&quot;</span>);    <span class="comment">//通过任务名获取任务句柄</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务2句柄：%#x\r\n&quot;</span>,(<span class="type">int</span>)task2_handle);</span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务2句柄：%#x\r\n&quot;</span>,(<span class="type">int</span>)task2_handler);</span><br><span class="line"> </span><br><span class="line"> UBaseType_t task_stack_min = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> eTaskState state = <span class="number">0</span>;</span><br><span class="line"> state = eTaskGetState(task2_handle);  <span class="comment">//根据句柄查询任务状态</span></span><br><span class="line"> Serial_Printf(<span class="string">&quot;任务2当前状态:%d\r\n&quot;</span>,state);</span><br><span class="line"></span><br><span class="line"> vTaskList( task_buff );</span><br><span class="line"> Serial_Printf(<span class="string">&quot;%s\r\n&quot;</span>,task_buff);</span><br><span class="line"> <span class="comment">//这里我修改了 Serial_Printf()函数定义中的 char String[100];  --&gt; char String[300];</span></span><br><span class="line"> <span class="comment">//同时增加了task2的 堆栈大小</span></span><br><span class="line"> <span class="comment">//按需要修改即可，如果过小，可能程序会卡死，即下面循环里的最小堆栈位不再打印或者led灯不再闪烁 </span></span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123; </span><br><span class="line">  task_stack_min = uxTaskGetStackHighWaterMark(task2_handler); <span class="comment">//当我写到这里的时候，前面的错误也找到了</span></span><br><span class="line">  Serial_Printf(<span class="string">&quot;task2剩余最小堆栈位%ld\r\n&quot;</span>,task_stack_min);   <span class="comment">//因为任务2的堆栈爆辣！！！！把TASK2_SIZE的64改成128就行</span></span><br><span class="line">  vTaskDelay(<span class="number">1000</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-5时间统计API函数"><a href="#B-5时间统计API函数" class="headerlink" title="B.5时间统计API函数"></a>B.5时间统计API函数</h3><p>在使用时间统计API函数之前，有以下流程</p><ol><li>将宏<code>configGENERATE_RUN_TIME_STATS</code> 置 1</li><li>将宏<code>configUSE_STATS_FORMATTING_FUNCTIONS</code> 置 1</li><li>然后还要实现两个宏定义：</li></ol><blockquote><ol><li>portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(): 用于初始化用于配置任务运行时间统计的时基定时器;<br>注意:这个时基定时器的计时精度需高于系统时钟节拍精度的10至100倍</li></ol></blockquote><blockquote><ol start="2"><li>portGET_RUN_TIME_COUNTER_VALUE(): 用于获取该功能时基硬件定时器计数的计数值。</li></ol></blockquote><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这个是在FreeRTOS的配置文件中</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> configGENERATE_RUN_TIME_STATS         1                       <span class="comment">//为1时启用运行时间统计功能</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;timer.h&quot;</span>  <span class="comment">//在这个文件中定义下面的函数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()               ConfigureTimeForRunTimeStats()</span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">uint32_t</span> FreeRTOSRunTimeTicks;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> portGET_RUN_TIME_COUNTER_VALUE()        FreeRTOSRunTimeTicks</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在timer.h文件中</span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> FreeRTOSRunTimeTicks = <span class="number">0</span>;  <span class="comment">//创建计数器</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">ConfigureTimeForRunTimeStats</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">/************************************  开启时钟TIM3  *****************************************************************/</span></span><br><span class="line"></span><br><span class="line"> RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);   <span class="comment">//开启TIM3的时钟</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*配置时钟源*/</span></span><br><span class="line"> TIM_InternalClockConfig(TIM3);  <span class="comment">//选择TIM3为内部时钟，若不调用此函数，TIM默认也为内部时钟</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*时基单元初始化*/</span></span><br><span class="line"> TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;    <span class="comment">//定义结构体变量</span></span><br><span class="line"> TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  <span class="comment">//时钟分频，选择不分频，此参数用于配置滤波器时钟，不影响时基单元功能</span></span><br><span class="line"> TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; <span class="comment">//计数器模式，选择向上计数</span></span><br><span class="line"> TIM_TimeBaseInitStructure.TIM_Period = <span class="number">10</span> - <span class="number">1</span>;    <span class="comment">//计数周期，即ARR的值</span></span><br><span class="line"> TIM_TimeBaseInitStructure.TIM_Prescaler = <span class="number">72</span> - <span class="number">1</span>;    <span class="comment">//预分频器，即PSC的值      T = (72Mhz/ARR+1)/(PSC+1)</span></span><br><span class="line"> TIM_TimeBaseInitStructure.TIM_RepetitionCounter = <span class="number">0</span>;   <span class="comment">//重复计数器，高级定时器才会用到</span></span><br><span class="line"> TIM_TimeBaseInit(TIM3, &amp;TIM_TimeBaseInitStructure);    <span class="comment">//将结构体变量交给TIM_TimeBaseInit，配置TIM3的时基单元 </span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*中断输出配置*/</span></span><br><span class="line"> TIM_ClearFlag(TIM3, TIM_FLAG_Update);      <span class="comment">//清除定时器更新标志位</span></span><br><span class="line">                <span class="comment">//TIM_TimeBaseInit函数末尾，手动产生了更新事件</span></span><br><span class="line">                <span class="comment">//若不清除此标志位，则开启中断后，会立刻进入一次中断</span></span><br><span class="line">                <span class="comment">//如果不介意此问题，则不清除此标志位也可</span></span><br><span class="line"> </span><br><span class="line"> TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);     <span class="comment">//开启TIM3的更新中断</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*NVIC中断分组*/</span></span><br><span class="line"> <span class="comment">//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);    //配置NVIC为分组4</span></span><br><span class="line">                <span class="comment">//即抢占优先级范围：0~`15，响应优先级范围：0</span></span><br><span class="line">                <span class="comment">//此分组配置在整个工程中仅需调用一次</span></span><br><span class="line">                <span class="comment">//若有多个中断，可以把此代码放在main函数内，while循环之前</span></span><br><span class="line">                <span class="comment">//若调用多次配置分组的代码，则后执行的配置会覆盖先执行的配置</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*NVIC配置*/</span></span><br><span class="line"> NVIC_InitTypeDef NVIC_InitStructure;      <span class="comment">//定义结构体变量</span></span><br><span class="line"> NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;    <span class="comment">//选择配置NVIC的TIM3线</span></span><br><span class="line"> NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    <span class="comment">//指定NVIC线路使能</span></span><br><span class="line"> NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = <span class="number">6</span>; <span class="comment">//指定NVIC线路的抢占优先级为6</span></span><br><span class="line"> NVIC_InitStructure.NVIC_IRQChannelSubPriority = <span class="number">0</span>;   <span class="comment">//指定NVIC线路的响应优先级为0</span></span><br><span class="line"> NVIC_Init(&amp;NVIC_InitStructure);        <span class="comment">//将结构体变量交给NVIC_Init，配置NVIC外设</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*TIM使能*/</span></span><br><span class="line"> TIM_Cmd(TIM3, ENABLE);   <span class="comment">//使能TIM3，定时器开始运行</span></span><br><span class="line"> FreeRTOSRunTimeTicks = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//计数器自加</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">TIM3_IRQHandler</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">if</span> (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)</span><br><span class="line"> &#123;</span><br><span class="line">  </span><br><span class="line">  TIM_ClearITPendingBit(TIM3, TIM_IT_Update);</span><br><span class="line">  FreeRTOSRunTimeTicks++;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>然后就可以直接调用<code>vTaskGetRunTimeStats();</code>函数，使用方法和上面的<code>vTaskList()</code>使用方法相同。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">vTaskGetRunTimeStats(task_buff);    <span class="comment">//获取运行时间统计函数的表格</span></span><br><span class="line">Serial_Printf(<span class="string">&quot;%s\r\n&quot;</span>,task_buff);  <span class="comment">//串口输出</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-6队列相关函数"><a href="#B-6队列相关函数" class="headerlink" title="B.6队列相关函数"></a>B.6队列相关函数</h3><p>这个就直接贴成功运行的代码了：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"> <span class="comment">/***********************************************************************************************/</span></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment"> *程序名称：队列操作实验</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">/***********************************************************************************************/</span></span><br><span class="line"> </span><br><span class="line"><span class="type">uint8_t</span> key_num;</span><br><span class="line">QueueHandle_t key_queue;      <span class="comment">// 小数据句柄</span></span><br><span class="line">QueueHandle_t big_data_queue; <span class="comment">// 大数据句柄</span></span><br><span class="line"><span class="type">char</span>  buff[<span class="number">100</span>] = <span class="string">&quot;难道说还是报错&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> START_TASK_PRIO   1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> START_TASK_SIZE   64</span></span><br><span class="line">TaskHandle_t start_task_handler;</span><br><span class="line"><span class="type">void</span> <span class="title function_">Start_Task</span><span class="params">(<span class="type">void</span> *pvParameters)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TASK1_PRIO   2</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TASK1_SIZE   64</span></span><br><span class="line">TaskHandle_t task1_handler;</span><br><span class="line"><span class="type">void</span> <span class="title function_">Task1</span><span class="params">(<span class="type">void</span> *pvParameters)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TASK2_PRIO   4</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TASK2_SIZE   256</span></span><br><span class="line">TaskHandle_t task2_handler;</span><br><span class="line"><span class="type">void</span> <span class="title function_">Task2</span><span class="params">(<span class="type">void</span> *pvParameters)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TASK3_PRIO   4</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TASK3_SIZE   256</span></span><br><span class="line">TaskHandle_t task3_handler;</span><br><span class="line"><span class="type">void</span> <span class="title function_">Task3</span><span class="params">(<span class="type">void</span> *pvParameters)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">freertos_demo</span><span class="params">()</span></span><br><span class="line">&#123;  </span><br><span class="line"> <span class="comment">//小数据队列创建</span></span><br><span class="line"> key_queue = xQueueCreate( <span class="number">2</span>, <span class="keyword">sizeof</span>(<span class="type">uint8_t</span>));  <span class="comment">//两个key 返回key的值大小为uint8_t</span></span><br><span class="line"> <span class="keyword">if</span>(key_queue != <span class="literal">NULL</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  Serial_Printf(<span class="string">&quot;小创建成功\r\n&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> Serial_Printf(<span class="string">&quot;小创建失败\r\n&quot;</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//大数据队列创建</span></span><br><span class="line"> big_data_queue = xQueueCreate( <span class="number">1</span>, <span class="keyword">sizeof</span>(<span class="type">char</span>*));  <span class="comment">//一个数组  返回地址大小为char*</span></span><br><span class="line"> <span class="keyword">if</span>(big_data_queue != <span class="literal">NULL</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  Serial_Printf(<span class="string">&quot;大创建成功\r\n&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> Serial_Printf(<span class="string">&quot;大创建失败\r\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"> xTaskCreate(</span><br><span class="line">    (TaskFunction_t    )         Start_Task,</span><br><span class="line">    (<span class="type">char</span>       *)        <span class="string">&quot;Start_Task&quot;</span>,</span><br><span class="line">    (configSTACK_DEPTH_TYPE   )       START_TASK_SIZE,</span><br><span class="line">    (<span class="type">void</span>       *)        <span class="literal">NULL</span>,</span><br><span class="line">    (UBaseType_t     )        START_TASK_PRIO,</span><br><span class="line">    (TaskHandle_t *     )        &amp;start_task_handler </span><br><span class="line">    );</span><br><span class="line"> vTaskStartScheduler();  <span class="comment">//开启任务调度器</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//开始任务:创建任务一，任务二和任务三</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Start_Task</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line">  taskENTER_CRITICAL(); <span class="comment">//进入临界区，即关闭比该任务优先级大的“中断任务”</span></span><br><span class="line">  Serial_SendString(<span class="string">&quot;start_ing\r\n&quot;</span>);</span><br><span class="line">  xTaskCreate(</span><br><span class="line">    (TaskFunction_t    )         Task1,</span><br><span class="line">    (<span class="type">char</span>       *)        <span class="string">&quot;Task1&quot;</span>,</span><br><span class="line">    (configSTACK_DEPTH_TYPE   )       TASK1_SIZE,</span><br><span class="line">    (<span class="type">void</span>       *)        <span class="literal">NULL</span>,</span><br><span class="line">    (UBaseType_t     )        TASK1_PRIO,</span><br><span class="line">    (TaskHandle_t *     )        &amp;task1_handler </span><br><span class="line">    );</span><br><span class="line">    </span><br><span class="line">  xTaskCreate(</span><br><span class="line">    (TaskFunction_t    )         Task2,</span><br><span class="line">    (<span class="type">char</span>       *)        <span class="string">&quot;Task2&quot;</span>,</span><br><span class="line">    (configSTACK_DEPTH_TYPE   )       TASK2_SIZE,</span><br><span class="line">    (<span class="type">void</span>       *)        <span class="literal">NULL</span>,</span><br><span class="line">    (UBaseType_t     )        TASK2_PRIO,</span><br><span class="line">    (TaskHandle_t *     )        &amp;task2_handler </span><br><span class="line">    );</span><br><span class="line">  xTaskCreate(</span><br><span class="line">    (TaskFunction_t    )         Task3,</span><br><span class="line">    (<span class="type">char</span>       *)        <span class="string">&quot;Task3&quot;</span>,</span><br><span class="line">    (configSTACK_DEPTH_TYPE   )       TASK3_SIZE,</span><br><span class="line">    (<span class="type">void</span>       *)        <span class="literal">NULL</span>,</span><br><span class="line">    (UBaseType_t     )        TASK3_PRIO,</span><br><span class="line">    (TaskHandle_t *     )        &amp;task3_handler </span><br><span class="line">    );</span><br><span class="line">    <span class="comment">//删除自身任务</span></span><br><span class="line">    vTaskDelete(<span class="literal">NULL</span>);</span><br><span class="line">    taskEXIT_CRITICAL(); <span class="comment">//退出临界区</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//任务一: 实现入队</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task1</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> BaseType_t err =<span class="number">0</span>; <span class="comment">//判断接受失败或成功</span></span><br><span class="line">    <span class="type">char</span> *data_to_send = buff;  <span class="comment">// 直接使用 buff 数据</span></span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  taskENTER_CRITICAL(); <span class="comment">//进入临界区，即关闭比该任务优先级大的“中断任务”</span></span><br><span class="line">  key_num = Key_GetNum();</span><br><span class="line">  <span class="keyword">if</span>(key_num == <span class="number">1</span> || key_num == <span class="number">2</span>)</span><br><span class="line">  &#123;</span><br><span class="line">   err = xQueueSend( key_queue, &amp;key_num, portMAX_DELAY );<span class="comment">//句柄，写入值的地址，延时（死等）</span></span><br><span class="line">   <span class="keyword">if</span>(err != pdTRUE)</span><br><span class="line">   &#123;</span><br><span class="line">    Serial_Printf(<span class="string">&quot;小发送失败\r\n&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span> <span class="keyword">if</span>(key_num == <span class="number">3</span>)</span><br><span class="line">  &#123;</span><br><span class="line">   err = xQueueSend( big_data_queue, &amp;data_to_send, portMAX_DELAY );<span class="comment">//句柄，写入值的地址，延时（死等）</span></span><br><span class="line">   <span class="keyword">if</span>(err != pdTRUE)</span><br><span class="line">   &#123;</span><br><span class="line">    Serial_Printf(<span class="string">&quot;大发送失败\r\n&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span></span><br><span class="line">   &#123;</span><br><span class="line">    Serial_Printf(<span class="string">&quot;大发送成功\r\n&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  taskEXIT_CRITICAL(); <span class="comment">//退出临界区</span></span><br><span class="line">  vTaskDelay(<span class="number">10</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//任务二：小数据出队</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task2</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> <span class="type">uint8_t</span> key_r = <span class="number">0</span>; <span class="comment">// 接受数据</span></span><br><span class="line"> BaseType_t err =<span class="number">0</span>; <span class="comment">//判断接受失败或成功</span></span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123;</span><br><span class="line"> <span class="comment">/*注意这个任务可以不加延时函数，因为当队列为空时会系统会自动切换任务二到阻塞态*/</span></span><br><span class="line">  err = xQueueReceive( key_queue,        <span class="comment">//句柄        </span></span><br><span class="line">                          &amp;key_r,      <span class="comment">//读取到-&gt;地址</span></span><br><span class="line">                          portMAX_DELAY );     <span class="comment">//死等</span></span><br><span class="line">  <span class="keyword">if</span>(err != pdTRUE)</span><br><span class="line">  &#123;</span><br><span class="line">   Serial_Printf(<span class="string">&quot;小接收失败\r\n&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">   Serial_Printf(<span class="string">&quot;小数据为：%d\r\n&quot;</span>,key_r);</span><br><span class="line">  &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//任务三: 大数据出队</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task3</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *buf = <span class="literal">NULL</span>;  <span class="comment">// 接收数据时的指针</span></span><br><span class="line">    BaseType_t err = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">   &#123;</span><br><span class="line">      err = xQueueReceive(big_data_queue, &amp;buf, portMAX_DELAY);</span><br><span class="line">      <span class="keyword">if</span>(err != pdTRUE)</span><br><span class="line">      &#123;</span><br><span class="line">          Serial_Printf(<span class="string">&quot;大数据接收失败\r\n&quot;</span>);</span><br><span class="line">       &#125;</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">      &#123;</span><br><span class="line">          UBaseType_t task_stack_min = uxTaskGetStackHighWaterMark(task2_handler);</span><br><span class="line">          <span class="comment">//Serial_Printf(&quot;任务2剩余最小堆栈空间：%ld\r\n&quot;, task_stack_min);  </span></span><br><span class="line">          Serial_Printf(<span class="string">&quot;大数据接收成功\r\n&quot;</span>);</span><br><span class="line">          Serial_Printf(<span class="string">&quot;%s\r\n&quot;</span>, buf);  <span class="comment">// 直接打印接收到的字符串</span></span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>暂时只在打印的时候遇到了问题(打印出现乱码)，解决方法见C.4；</p><h3 id="B-7信号量"><a href="#B-7信号量" class="headerlink" title="B.7信号量"></a>B.7信号量</h3><h4 id="信号量简介"><a href="#信号量简介" class="headerlink" title="信号量简介"></a>信号量简介</h4><p>信号量是一种解决同步问题的机制，可以实现对共享资源的有序访问</p><blockquote><p>信号量（计数值）&gt;1 代表有资源<br>当释放信号量，计数值就加1<br>当获取信号量，计数值就减1<br>信号量的作用就是用来传递状态</p></blockquote><p>信号量的计数值会有限制：限定最大值<br>如果最大值为1，则它是<strong>二值信号量</strong><br>如果最大值不是1，则它是<strong>计数型信号量</strong></p><h4 id="二值信号量"><a href="#二值信号量" class="headerlink" title="二值信号量"></a>二值信号量</h4><p>二值信号量的本质是一个队列长度为1的队列，队列只有空和满两种情况。<br>通常用于互斥访问或任务同步，与互斥信号量比较类型，但是二值信号量<br>有可能会<strong>导致优先级翻转的问题</strong>，所以更适合用于同步。</p><p>创建二值信号量：<br>动态：<code>xSemaphoreCreateBinary(void)</code><br>静态：<code>xSemaphoreCreateBinaryStatic(void)</code><br>返回值：<strong>NULL</strong> 表示创建失败 <strong>其它</strong>表示二值信号量的句柄</p><p>释放二值信号量：<strong>相当于把该标志置”满”</strong><br>任务级：<code>xSemaphoreGive()</code><br>中断级：<code>xSemaphoreGiveFromISR()</code><br>形参：<strong>要释放的信号量句柄</strong>，不支持设置阻塞时间<br>返回值：<strong>pdPASS</strong>表示释放成功，<strong>errQUEUE_FULL</strong>表示释放失败</p><p>获取二值信号量：<strong>相当于把该标志置”空”</strong><br>任务级：<code>xSemaphoreTake()</code><br>中断级：<code>xSemaphoreTakeFromISR()</code><br>形参：<strong>要释放的信号量句柄</strong>,<strong>阻塞时间</strong><br>返回值：<strong>pdTRUE</strong>表示获取成功，<strong>pdFALSE</strong>表示获取失败</p><h4 id="二值信号量API函数实验"><a href="#二值信号量API函数实验" class="headerlink" title="二值信号量API函数实验"></a>二值信号量API函数实验</h4><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//部分代码</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">include</span> <span class="string">&quot;semphr.h&quot;</span> <span class="comment">//相关头文件</span></span></span><br><span class="line"> <span class="comment">//创建二值信号量</span></span><br><span class="line"> semphore_handle = xSemaphoreCreateBinary();</span><br><span class="line"> <span class="keyword">if</span>(semphore_handle != <span class="literal">NULL</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  Serial_Printf(<span class="string">&quot;创建成功\r\n&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//任务一: 按键扫描，检查到按钮1按下，释放二值信号量</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task1</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> BaseType_t err = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  taskENTER_CRITICAL(); <span class="comment">//进入临界区，即关闭比该任务优先级大的“中断任务”</span></span><br><span class="line">  key_num = Key_GetNum();</span><br><span class="line">  <span class="keyword">if</span>(key_num == <span class="number">1</span>)</span><br><span class="line">  &#123;</span><br><span class="line">   <span class="keyword">if</span>(semphore_handle != <span class="literal">NULL</span>)</span><br><span class="line">   &#123;</span><br><span class="line">    err = xSemaphoreGive(semphore_handle);<span class="comment">//释放信号量</span></span><br><span class="line">    <span class="keyword">if</span>(err == pdPASS)</span><br><span class="line">    &#123;</span><br><span class="line">     Serial_Printf(<span class="string">&quot;释放成功\r\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span></span><br><span class="line">   &#123;</span><br><span class="line">     Serial_Printf(<span class="string">&quot;释放失败\r\n&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  taskEXIT_CRITICAL(); <span class="comment">//退出临界区</span></span><br><span class="line">  vTaskDelay(<span class="number">10</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//任务二：获取二值信号量，获取成功后打印提示信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task2</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> <span class="type">uint32_t</span> i = <span class="number">0</span>;</span><br><span class="line"> BaseType_t err = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="comment">//如果获取不到信号量，就死等，进入阻塞态</span></span><br><span class="line">  err = xSemaphoreTake(semphore_handle,portMAX_DELAY);<span class="comment">//获取信号量</span></span><br><span class="line">  <span class="keyword">if</span>(err == pdTRUE)</span><br><span class="line">  &#123;</span><br><span class="line">   Serial_Printf(<span class="string">&quot;获取成功:%d\r\n&quot;</span>,i++);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span> <span class="keyword">if</span>(err == pdFALSE) Serial_Printf(<span class="string">&quot;获取失败&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="计数型信号量"><a href="#计数型信号量" class="headerlink" title="计数型信号量"></a>计数型信号量</h4><p>计数型信号量相当于队列大于1的队列。因此计数型信号量可以容纳多个资源，队列长度是在创建它时确定的</p><p>使用过程：创建计数型信号量-&gt;释放信号量-&gt;获取信号量</p><p>创建二值信号量：<br>动态：<code>xSemaphoreCreateCounting()</code><br>静态：<code>xSemaphoreCreateCountingStatic()</code><br>形参：<strong>uxMaxCount</strong>计数值的最大限定值，<strong>uxInitialCount</strong>计数值的初始值<br>返回值：<strong>NULL</strong> 表示创建失败 <strong>其它</strong>表示二值信号量的句柄</p><p>获取信号量的计数值：<br><code>xSemaphoreGetCount()</code></p><p>释放获取信号量的API函数与<strong>二值信号量相同</strong></p><h4 id="优先级翻转"><a href="#优先级翻转" class="headerlink" title="优先级翻转"></a>优先级翻转</h4><p>简介：高优先级的任务反而慢执行，低优先级的任务反而先执行<br>在抢占式内核中很常见，但是在实时操作系统中是不允许出现的，会破坏任务的预期顺序可能会导致位置的严重后果</p><p>在使用二值信号量的时候，可能会导致优先级翻转</p><blockquote><p>*start_task： 创建task1和taks2和task3<br>*task1：   低优先级任务，先获取信号量，然后执行一段较长时间的任务，然后释放信号量<br>*task2：   中等优先级任务，简单的应用任务<br>*task3:    高优先级任务，先获取信号量，然后执行一段拍普通时间的任务，然后释放信号量<br><strong>比如在运行这个程序的时候，当task1获得信号量时，执行任务的时候，被高优先级task3抢占，但是因为task1还没有释放，所以task3进入阻塞，一直到task1释放，task3才可以运行，优先级发生了翻转，具体现象见实验13</strong></p></blockquote><h4 id="互斥信号量"><a href="#互斥信号量" class="headerlink" title="互斥信号量"></a>互斥信号量</h4><p>互斥信号量是一个拥有<strong>优先级继承</strong>的二值信号量，在同步的应用中使用二值信号量最适合，而互斥信号量适合用于就那些需要互斥访问的应用中。可以用来解决二值信号量的优先级翻转的问题</p><blockquote><p>优先级继承</p></blockquote><p>当一个互斥信号量正在被一个低优先级的任务持有时，如果此时有个高优先级的任务也尝试获取这个互斥信号量，那么这个高优先级的任务就会被阻塞。<strong>但是这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。</strong><br><strong>互斥信号量不能用于中断服务函数，整你用于任务中</strong></p><p>使用流程：创建互斥信号量-&gt;（task）获取信号量-&gt;（give）释放信号量<br><strong>注意互斥信号量创建后会主动释放一次信号量</strong></p><p>创建互斥信号量：<br>动态：<code>xSemaphoreCreateMutex()</code><br>静态：<code>xSemaphoreCreateMutexStatic()</code><br>形参：<strong>uxMaxCount</strong>计数值的最大限定值，<strong>uxInitialCount</strong>计数值的初始值<br>返回值：<strong>NULL</strong> 表示创建失败 <strong>其它</strong>表示二值信号量的句柄</p><p>释放获取信号量的API函数与<strong>二值信号量相同</strong>，但是<strong>不支持中断中调用</strong></p><h3 id="B-8队列集"><a href="#B-8队列集" class="headerlink" title="B.8队列集"></a>B.8队列集</h3><p>用来解决在一个任务中同时要使用多个队列的情况。如果按照之前的用法，在第一个队列没有获得消息时会阻塞，这样下一个队列即使有信息也无法执行。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//定义句柄</span></span><br><span class="line">QueueSetHandle_t queueset_handle;</span><br><span class="line">QueueHandle_t queue_handle;</span><br><span class="line">QueueHandle_t semphr_handle;</span><br><span class="line"></span><br><span class="line"><span class="comment">//任务一：创建队列，信号量和队列集</span></span><br><span class="line"> queueset_handle = xQueueCreateSet( <span class="number">2</span> );   <span class="comment">//创建队列集</span></span><br><span class="line"> <span class="keyword">if</span>(queueset_handle != <span class="literal">NULL</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  Serial_Printf(<span class="string">&quot;队列集创建成功\r\n&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> Serial_Printf(<span class="string">&quot;队列集创建失败\r\n&quot;</span>);</span><br><span class="line"> </span><br><span class="line"> queue_handle = xQueueCreate( <span class="number">1</span>, <span class="keyword">sizeof</span>(<span class="type">uint8_t</span>) ) ;<span class="comment">//创建队列</span></span><br><span class="line"> semphr_handle = xSemaphoreCreateBinary();      <span class="comment">//创建信号量</span></span><br><span class="line"> </span><br><span class="line"> xQueueAddToSet(queue_handle,queueset_handle);   <span class="comment">//添加到队列集</span></span><br><span class="line"> xQueueAddToSet(semphr_handle,queueset_handle);  <span class="comment">//添加到队列集</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//任务二：获取队列信息</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task2</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="type">uint8_t</span> key_r = <span class="number">0</span>; <span class="comment">// 接受数据</span></span><br><span class="line">  QueueSetMemberHandle_t member_handle; </span><br><span class="line">  member_handle  = xQueueSelectFromSet( queueset_handle,portMAX_DELAY); <span class="comment">//返回队列句柄</span></span><br><span class="line">  <span class="keyword">if</span>(member_handle == queue_handle)</span><br><span class="line">  &#123;</span><br><span class="line">   xQueueReceive(member_handle,&amp;key_r,portMAX_DELAY);</span><br><span class="line">   Serial_Printf(<span class="string">&quot;获取到的队列数据为%d\r\n&quot;</span>,key_r);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span> <span class="keyword">if</span>(member_handle == semphr_handle)</span><br><span class="line">  &#123;</span><br><span class="line">   xSemaphoreTake(semphr_handle,portMAX_DELAY);</span><br><span class="line">   Serial_Printf(<span class="string">&quot;获取信号量成功！\r\n&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-9事件标志组"><a href="#B-9事件标志组" class="headerlink" title="B.9事件标志组"></a>B.9事件标志组</h3><p>用一个32位字符的前24位的（0或1）表示事件是否发生，然后执行相关操作</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//宏定义标志位，后续使用方便</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EVENTBIT_0 (1 &lt;&lt; 0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EVENTBIT_1 (1 &lt;&lt; 1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> EVENTBIT_2 (1 &lt;&lt; 2)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//创建标志位组</span></span><br><span class="line">eventgroup_handle =  xEventGroupCreate();</span><br><span class="line"></span><br><span class="line"><span class="comment">//任务一: 按键扫描，根据不同的键值将时事件标志组相应的事件置一，模拟事件发生</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task1</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  key_num = Key_GetNum();</span><br><span class="line">  <span class="keyword">if</span>(key_num == <span class="number">1</span>)</span><br><span class="line">  &#123;</span><br><span class="line">   xEventGroupSetBits( eventgroup_handle,EVENTBIT_0); <span class="comment">//将指定位置1，表示事件已经发生</span></span><br><span class="line">   Serial_Printf(<span class="string">&quot;0\r\n&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span>(key_num == <span class="number">2</span>)</span><br><span class="line">  &#123;</span><br><span class="line">   xEventGroupSetBits( eventgroup_handle,EVENTBIT_1);</span><br><span class="line">   Serial_Printf(<span class="string">&quot;1\r\n&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span>(key_num == <span class="number">3</span>)</span><br><span class="line">  &#123;</span><br><span class="line">   xEventGroupSetBits( eventgroup_handle,EVENTBIT_2);</span><br><span class="line">   Serial_Printf(<span class="string">&quot;2\r\n&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  vTaskDelay(<span class="number">10</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//任务二：同时等待事件标志组中的多个事件位，当这些事件全部为1时就执行相应的处理</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Task2</span><span class="params">(<span class="type">void</span> *pvParameters)</span></span><br><span class="line">&#123;</span><br><span class="line"> EventBits_t event_bit;</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> &#123;</span><br><span class="line">  event_bit= xEventGroupWaitBits( eventgroup_handle,       <span class="comment">/*事件标志组句柄*/</span></span><br><span class="line">          EVENTBIT_0 | EVENTBIT_1 | EVENTBIT_2 ,           <span class="comment">/*等待事件标志组的bit0，bit1，bit2*/</span></span><br><span class="line">          pdTRUE,                                          <span class="comment">/*成功等待到后清楚事件bit0，bit1，bit2标志位*/</span></span><br><span class="line">          pdTRUE,                                          <span class="comment">/*等待事件标志组bit0，bit1，bit2都发生*/</span></span><br><span class="line">          portMAX_DELAY);                                  <span class="comment">/*死等*/</span></span><br><span class="line">  Serial_Printf(<span class="string">&quot;等待的事件标志位值为:%#x\r\n&quot;</span>,event_bit);   <span class="comment">/*bit0，bit1，bit2或起来应该是0x7*/</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="B-10样例实验"><a href="#B-10样例实验" class="headerlink" title="B.10样例实验"></a>B.10样例实验</h3><ol><li>任务的创建和删除（动态）</li><li>任务的创建和删除（静态）</li><li>任务挂起和恢复</li><li>中断管理</li><li>列表项的插入和删除实现</li><li>时间片调度</li><li>任务相关API函数</li><li>任务时间统计API函数</li><li>延时函数延时</li><li>队列相关API函数</li><li>二值信号量</li><li>计数型信号量</li><li>模拟优先级翻转</li><li>互斥信号量</li><li>队列集</li><li>事件标志组</li></ol><h2 id="C遇到的问题"><a href="#C遇到的问题" class="headerlink" title="C遇到的问题"></a>C遇到的问题</h2><h3 id="C-1在实验1-2中oled屏幕显示出现部分错位花屏等现象"><a href="#C-1在实验1-2中oled屏幕显示出现部分错位花屏等现象" class="headerlink" title="C.1在实验1,2中oled屏幕显示出现部分错位花屏等现象"></a>C.1在实验1,2中oled屏幕显示出现部分错位花屏等现象</h3><blockquote><p>问题原因:</p></blockquote><p>oled 屏幕显示函数执行的时间不够，因切换任务而被打断。</p><blockquote><p>解决方法：</p></blockquote><ol><li>添加函数<strong>taskENTER_CRITICAL()<strong>和</strong>taskEXIT_CRITICAL()</strong> 在显示的时候进入临界区，防止被打断。</li><li>在比oled高优先级任务中**增加vTaskDelay()**函数的时长，这么显示函数的执行时间便会增长。</li></ol><h3 id="C-2在实验7中，出现报错：-OBJ-LED-axf-Error-L6915E-Library-reports-error-use-no-semihosting-was-requested-but-ttywrch-was-referenced"><a href="#C-2在实验7中，出现报错：-OBJ-LED-axf-Error-L6915E-Library-reports-error-use-no-semihosting-was-requested-but-ttywrch-was-referenced" class="headerlink" title="C.2在实验7中，出现报错：..\OBJ\LED.axf: Error: L6915E: Library reports error: __use_no_semihosting was requested, but _ttywrch was referenced"></a>C.2在实验7中，出现报错：..\OBJ\LED.axf: Error: L6915E: Library reports error: __use_no_semihosting was requested, but _ttywrch was referenced</h3><blockquote><p>解决方法</p></blockquote><p>1：在魔术棒中<strong>Target</strong>选项中 将<strong>Use MicroLIB</strong>勾选上，然后编译没有报错</p><blockquote><p>问题原因</p></blockquote><p>因为使用了<strong>malloc</strong>函数，反之如果想使用malloc函数就需要勾选上面这个选项并<strong>include “stdlib.h”</strong></p><h3 id="C-3在实验7中，调用uxTaskGetSystemState-函数出现问题"><a href="#C-3在实验7中，调用uxTaskGetSystemState-函数出现问题" class="headerlink" title="C.3在实验7中，调用uxTaskGetSystemState()函数出现问题"></a>C.3在实验7中，调用uxTaskGetSystemState()函数出现问题</h3><blockquote><p>问题描述</p></blockquote><p>函数执行完毕后 打印结果里面并没有 task1 任务的信息  或者 是 task1 任务的信息有误（任务编号无穷大&#x2F;任务名乱码）</p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Embedded_1\word_00001_020.webp"                      width="800"                 ><p>尝试直接不创建任务1，其它代码不变</p><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Embedded_1\word_00001_021.webp"                      width="800"                 ><p>使用vTaskGetInfo() 函数 查询Task1 依旧有错误</p><blockquote><p>问题原因</p></blockquote><p>执行这些查询操作函数的任务2的<strong>堆栈爆了</strong>，所以报错<br>使用 <strong>uxTaskGetStackHighWaterMark()</strong> 函数查询task2的时候发现的<br>当时定义的64，但实际需使用至少78，所以爆了。</p><blockquote><p>解决方法</p></blockquote><p>使用 <strong>uxTaskGetStackHighWaterMark()</strong> 函数查询各个任务，将堆栈大小设置在合理范围即可</p><h3 id="C-4在实验10中大数据打印出现乱码"><a href="#C-4在实验10中大数据打印出现乱码" class="headerlink" title="C.4在实验10中大数据打印出现乱码"></a>C.4在实验10中大数据打印出现乱码</h3><blockquote><p>问题描述</p></blockquote><p>在打印的时候遇到了问题(打印出现乱码)<br><code>Serial_Printf(&quot;%s\r\n&quot;, buf);  // 直接打印接收到的字符串</code></p><blockquote><p>问题原因</p></blockquote><p>原因是我在创建大数据数组的时候<br><code>char  buff[100] = &quot;难道说还是报错&quot;;</code><br><code>err = xQueueSend(big_data_queue, &amp;buff, portMAX_DELAY);</code><br>在 Task1 中将数据 “难道说还是报错” 作为 char* 类型的指针传入队列，<br>这样会导致队列存储的是一个指向字符串的指针，而不是字符串本身。队列<br>中的数据是一个指针，接收端 Task3 正确地接收了这个指针，但没有正确地<br>获取到实际的字符串内容,而导致打印出来可能会出现乱码。</p><blockquote><p>解决方法</p></blockquote><p>在任务1中发送字符串内容（通过指针传递）而不是发送指针本身。<br>添加一行代码：<br><code>char *data_to_send = buff;  // 直接使用 buff 数据</code><br>修改一行代码：<br><code>err = xQueueSend(big_data_queue, &amp;data_to_send, portMAX_DELAY);</code></p><h2 id="D多线程项目"><a href="#D多线程项目" class="headerlink" title="D多线程项目"></a>D多线程项目</h2><h2 id="E结尾"><a href="#E结尾" class="headerlink" title="E结尾"></a>E结尾</h2><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><blockquote><ol><li><a class="link"   href="https://www.bilibili.com/video/BV19g411p7UT/?spm_id_from=333.337.search-card.all.click&vd_source=cc095291c6db5e4f5f537249666b6ccc" >【正点原子】手把手教你学FreeRTOS实时系统<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link"   href="https://www.freertos.org/zh-cn-cmn-s/Documentation/00-Overview" >FreeRTOS官方文档<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link"   href="https://blog.csdn.net/cairongshou/article/details/129090567?fromshare=blogdetail&sharetype=blogdetail&sharerId=129090567&sharerefer=PC&sharesource=hun_hao&sharefrom=from_link" >基于STM32F103标准库移植FreeRTOS教程<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="文件地址"><a href="#文件地址" class="headerlink" title="文件地址"></a>文件地址</h3><blockquote><ol><li><a class="link"   href="https://github.com/Hunhaozi/FreeRTOS-Study" >github仓库<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link"   href="https://pan.baidu.com/s/1ZqnSnFiE_WCu4ad8MnuZAQ?pwd=92jt" >网盘地址 提取码: 92jt<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><blockquote><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link"   href="mailto:&#x33;&#49;&#x34;&#x36;&#55;&#48;&#50;&#51;&#x36;&#50;&#64;&#x71;&#113;&#x2e;&#x63;&#x6f;&#x6d;" >邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">FreeRTOS 是一款市场领先的开源的嵌入式系统，支持 40 多种处理器架构，内存占用小，执行时间快，具有尖端的 RTOS 功能和库，包括对称多处理 (SMP)、具有 IPv6 支持的线程安全 TCP 堆栈以及与云服务的无缝集成，并得到了积极的支持和维护。</summary>
    
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/categories/Embedded/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="FreeRTOS" scheme="https://blog.haozi-haozi.cn/tags/FreeRTOS/"/>
    
  </entry>
  
  <entry>
    <title>Raspi——树莓派——入手遇到的问题合集</title>
    <link href="https://blog.haozi-haozi.cn/2024/10/22/raspi_problem/"/>
    <id>https://blog.haozi-haozi.cn/2024/10/22/raspi_problem/</id>
    <published>2024-10-22T00:20:24.000Z</published>
    <updated>2026-03-24T08:57:19.068Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-引入"><a href="#A-引入" class="headerlink" title="A 引入"></a>A 引入</h2><h3 id="A-1-树莓派的型号及系统安装"><a href="#A-1-树莓派的型号及系统安装" class="headerlink" title="A.1 树莓派的型号及系统安装"></a>A.1 树莓派的型号及系统安装</h3><p>我入手的树莓派版本是 <strong>4B&#x2F;4GB</strong> 的，使用的系统为树莓派官网推荐的 <strong>Raspberry Pi OS</strong> 的最新版<strong>64位</strong>（当前时间为2024&#x2F;10&#x2F;22）<a class="link"   href="https://www.raspberrypi.com/software/" >官网下载地址<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> 后面的所有环境皆为这个版本<br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_010.webp"                       width="800"                 ><br>然后根据步骤选择烧录到树莓派中<br>注意：</p><blockquote><ol><li>如果是自带屏幕的可以在烧录器中不选择设置账号密码以及ssh，可以直接启动后在屏幕上操作。</li><li>不带屏幕的，可以提前设置好，方便后续用自己电脑的VNC连接树莓派。同时可以设置好WiFi连接，可以是自己手机的热点，这样更容易获取树莓派的IP地址。</li><li>VNC软件可以直接下载官网推荐的<a class="link"   href="https://www.realvnc.com/en/connect/download/viewer/" >VNC Viewer 下载地址<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li>启动烧录好的树莓派，然后电脑vnc连接，设置中文输入法等问题少的步骤可以自行搜索教程，入门教程推荐b站 <a class="link"   href="https://www.bilibili.com/video/BV16U4y1879Q/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click" >树莓派教程第一课 树莓派简介 十分钟玩转系列入门篇<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>，下面主要介绍我当时遇到的问题。</li></ol><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_011.webp"                      width="800"                ></blockquote><h2 id="B-问题合集"><a href="#B-问题合集" class="headerlink" title="B 问题合集"></a>B 问题合集</h2><h3 id="B-1-快速换源（apt-和-pip）"><a href="#B-1-快速换源（apt-和-pip）" class="headerlink" title="B.1 快速换源（apt 和 pip）"></a>B.1 快速换源（apt 和 pip）</h3><blockquote><p>解决下载东西过慢的问题，同时适合于新手首次操作的换源步骤</p></blockquote><ul><li>apt换源<br>选择阿里云为镜像源，直接替换部分网址</li></ul><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> /etc/apt/sources.list /etc/apt/sources.list.bak</span><br><span class="line"><span class="built_in">sudo</span> apt update</span><br></pre></td></tr></table></figure></div><ul><li>pip换源</li></ul><ol><li>在Home目录创建 <strong>.pip</strong> 文件夹</li><li>在该文件下创建 pip.conf 文件</li></ol><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_012.webp"                      width="700"                >3. 里面写入以下代码<div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[global]</span><br><span class="line">index-url = https://pypi.doubanio.com/simple/</span><br><span class="line">timeout = 1000</span><br><span class="line">[install]</span><br><span class="line">use-mirrors = true</span><br><span class="line">mirrors = https://pypi.doubanio.com//</span><br></pre></td></tr></table></figure></div><ol start="4"><li>保存退出即可，其中1,2步骤代码就不写了，因为有图形化界面，详情学习一下linux基础指令就行</li></ol><h3 id="B-2-git-clone-克隆失败问题——下载WirngPi"><a href="#B-2-git-clone-克隆失败问题——下载WirngPi" class="headerlink" title="B.2 git clone 克隆失败问题——下载WirngPi"></a>B.2 git clone 克隆失败问题——下载WirngPi</h3><p>当时下载 WiringPi 的时候，其中有一步是</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/WiringPi/WiringPi.git </span><br></pre></td></tr></table></figure></div><p>但是我会报错 <strong>Couldn’t connect to server</strong>，最后查询网络找到的解决方法是：取消git代理,然后测试恢复正常</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git config --global --unset http.proxy</span><br><span class="line">git config --global --unset https.proxy</span><br></pre></td></tr></table></figure></div><blockquote><p>下面是安装 WiringPi 库的步骤</p></blockquote><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install git-core</span><br><span class="line">sudo apt-get update</span><br><span class="line">sudo apt-get upgrade</span><br><span class="line">git clone https://github.com/WiringPi/WiringPi</span><br><span class="line">cd ~/wiringPi</span><br><span class="line">git pull origin</span><br><span class="line">$ ./build</span><br></pre></td></tr></table></figure></div><p>下载编译无问题之后，可以尝试下面的指令，可以显示出树莓派的引脚图</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gpio readall</span><br></pre></td></tr></table></figure></div><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_013.webp"                      width="700"                ><h3 id="B-3-Python-pip安装报错"><a href="#B-3-Python-pip安装报错" class="headerlink" title="B.3 Python pip安装报错"></a>B.3 Python pip安装报错</h3><blockquote><p>Python安装报错： error: externally-managed-environment，This environment is externally managed<br>这个错误信息表示当前Python环境是由系统外部管理的，通常在某些Linux发行版中（尤其是Debian和基于Debian的系统，比如Ubuntu），系统会强烈建议不要直接使用pip来安装包，以避免与系统包管理器（如apt）的潜在冲突。</p></blockquote><blockquote><p>解决方法：创建一个虚拟环境</p></blockquote><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"># 安装python3-venv包，如果还没安装的话</span><br><span class="line">sudo apt install python3-venv</span><br><span class="line"></span><br><span class="line"># 创建一个新的虚拟环境</span><br><span class="line">python3 -m venv myenv</span><br><span class="line"></span><br><span class="line"># 激活虚拟环境</span><br><span class="line">source myenv/bin/activate</span><br><span class="line"></span><br><span class="line"># 现在你可以在虚拟环境中安装requests等</span><br><span class="line">pip install requests</span><br><span class="line"></span><br><span class="line">#退出虚拟环境</span><br><span class="line">deactivate</span><br></pre></td></tr></table></figure></div><h3 id="B-4-Camera——摄像头相关问题（Opencv）"><a href="#B-4-Camera——摄像头相关问题（Opencv）" class="headerlink" title="B.4 Camera——摄像头相关问题（Opencv）"></a>B.4 Camera——摄像头相关问题（Opencv）</h3><blockquote><p>在开启的选项系统下的<code>raspi-config</code>下的<code>interface options</code>中并没有找到<code>camera</code>选项</p></blockquote><p>原因:查资料得老版本的树莓派系统通过interface options中打开 cameraQ 使能是通过picamera这个库来使用的,但是2023年的树莓派新系统是基于debian12开发的,并且去除了picamera这个库.所以并不能通过interface options这里来打开camera.然后新系统本身预装了picamera2这个库（picamera库的升级版本）,然后这个picamera2库是基于libcamera开源框架.</p><blockquote><p>所以libcamera能成功调用摄像头的话.则可以通过picamera2的接口来编写程序来调用摄像头</p></blockquote><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install libcamera-apps</span><br><span class="line">sudo apt install libcamera-tools</span><br></pre></td></tr></table></figure></div><p>使用下面代码可以测试摄像头是否能使用</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">libcamera-hello</span><br></pre></td></tr></table></figure></div><p>如果不行的话,说明确实存在硬件连接方面的问题,请检查线路连接和摄像头是否正常<br>opencv模块的基础使用在 <code>B.6</code> 的参考链接中</p><h3 id="B-5-Picamera2模块（以及Opencv和Face-Recognition）的使用"><a href="#B-5-Picamera2模块（以及Opencv和Face-Recognition）的使用" class="headerlink" title="B.5 Picamera2模块（以及Opencv和Face Recognition）的使用"></a>B.5 Picamera2模块（以及Opencv和Face Recognition）的使用</h3><ul><li><strong>Picamera2     &ensp;&ensp;&ensp;调用摄像头</strong></li><li><strong>opencv       &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;进行人脸&#x2F;图像检测</strong></li><li><strong>Recognition&ensp;&ensp;进行人脸识别</strong></li></ul><blockquote><p>后面的基本过程可以 &ensp;<a class="link"   href="https://helloyu.top/raspberrypi/use-camera-with-python-picamera2/" >参考这个文章<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a><br>当然，也可以追溯 &ensp;&ensp;&ensp;<a class="link"   href="https://www.tomshardware.com/how-to/raspberry-pi-facial-recognition" >最初的文章<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p></blockquote><p>这个过程中遇到的问题文章中基本都提及并且能解决。</p><h3 id="B-6-复制-乱码问题——Xshell报错"><a href="#B-6-复制-乱码问题——Xshell报错" class="headerlink" title="B.6 复制-&gt;乱码问题——Xshell报错"></a>B.6 复制-&gt;乱码问题——Xshell报错</h3><p><strong>1. windows代码到linux的乱码问题</strong><br>原因是因为两个平台的编码格式不同<br>这个问题解决办法可以是将windows的字体调成UTF-8再复制,右键打开windows设置，然后根据下面步骤:<br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_014.webp"                       width="700"                 ><br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_015.webp"                       width="700"                 ><br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_016.webp"                       width="700"                 ><br><img                       lazyload                     src="/images/loading.svg"                     data-src="\img\blog_word\Raspi_1\word_00001_017.webp"                       width="700"                 ><br><strong>2. Xshell报错Program has stopped working问题</strong><br>解决办法：按上面步骤<strong>关掉这个beta测试选项</strong>。<br>其实复制有乱码自己打字或者提前写成相应编码格式或者直接传文件即可</p><h2 id="C-结尾"><a href="#C-结尾" class="headerlink" title="C 结尾"></a>C 结尾</h2><blockquote><h3 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h3><ol><li><a class="link"   href="https://www.tomshardware.com/how-to/raspberry-pi-facial-recognition" >人脸检测<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>  </li><li><a class="link"   href="https://helloyu.top/raspberrypi/use-camera-with-python-picamera2/" >Prcamera2使用<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote><blockquote><h3 id="留言"><a href="#留言" class="headerlink" title="留言"></a>留言</h3><p><strong>有问题请指出,你可以选择以下方式：</strong></p><ol><li>在下方评论区留言</li><li><a class="link"   href="mailto:&#x33;&#49;&#52;&#54;&#x37;&#48;&#x32;&#51;&#x36;&#50;&#x40;&#x71;&#113;&#46;&#x63;&#x6f;&#x6d;" >邮箱留言<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ol></blockquote>]]></content>
    
    
    <summary type="html">本篇为树莓派的学习笔记，将列举出在入手树莓派后遇到的bug和问题以及对应解决方法，还有opencv的使用过程等。</summary>
    
    
    
    <category term="树莓派" scheme="https://blog.haozi-haozi.cn/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
    <category term="Embedded" scheme="https://blog.haozi-haozi.cn/tags/Embedded/"/>
    
    <category term="Raspi" scheme="https://blog.haozi-haozi.cn/tags/Raspi/"/>
    
    <category term="opencv" scheme="https://blog.haozi-haozi.cn/tags/opencv/"/>
    
  </entry>
  
</feed>
