Skip to content

Commit

Permalink
Site updated: 2024-04-14 16:22:13
Browse files Browse the repository at this point in the history
  • Loading branch information
username committed Apr 14, 2024
1 parent 3297d80 commit 707a920
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 12 deletions.
1 change: 1 addition & 0 deletions 2023/03/13/cmu15445$lab5/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ <h1 class="post-title">源码解读</h1>
<div class="post-content">
<h1 id="源码解读"><a href="#源码解读" class="headerlink" title="源码解读"></a>源码解读</h1><p><img src="/2023/03/13/cmu15445/image-20231227153858926.png" alt="image-20231227153858926"></p>
<p>对实验中未涉及的parser、binder、planner、storage等部分进行源码的阅读。</p>
<p>以前一直觉得事务是个很抽象的东西,看了bustub实现,才总算窥见了一点真相……我以前觉得事务是一个包含了执行代码的某个执行体,一直很好奇它具体是怎么实现的。现在才发现,事务其实并不算执行体,它只是相当于一个record,对某个执行过程进行一系列记录和维护,比如说维护读写集、维护锁集之类的。这样,当整个执行体结束之后,它就记录了整个执行体的结果,从而可以对其进行复原和释放锁之类的了,相当于是一个统筹全局的爸爸。感觉这也体现了一种解耦思想,将代码的执行和与数据库的交互(读写、锁)解耦,是一个非常伟大的思想。</p>

</div>

Expand Down
12 changes: 6 additions & 6 deletions 2024/04/10/read_linux/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ <h3 id="24-4-12"><a href="#24-4-12" class="headerlink" title="24.4.12"></a>24.4.
</li>
</ol>
<p>其中,第二种情况稍显复杂,我们可以详细来说说。【下以deadline为例,因为别的比如说RR是非抢占式调度】</p>
<p>首先,全局只有一个调度类对象(不考虑什么多个调度策略,或者说一个线程组只有一个调度类对象,差不多这个意思)。</p>
<p>首先,全局只有一个调度类对象(不考虑什么多个调度策略,或者说一个线程组只有一个调度类对象,差不多这个意思)。也就是说,创建线程会进入该调度类对象中,然后挑选线程也是从该调度类对象中pick。</p>
<p>当一个CPU上运行的线程A创建了另一个线程B,并且假设目前其他CPU跑满了任务,线程B的优先级很高。创建线程的相关syscall最终会调用<code>activate_task</code>【这点暂时存疑】,然后<code>activate_task</code>会调用调度类中的<code>enqueue_task</code>事件,将线程B压入到deadline调度类的红黑树中。<code>activate_task</code>结束返回之后,紧随其后一般会接一个<code>resched_curr</code>,也即将当前CPU reschedule。</p>
<p>这里,就需要分情况讨论了。</p>
<ol>
Expand Down Expand Up @@ -288,16 +288,16 @@ <h3 id="24-4-12"><a href="#24-4-12" class="headerlink" title="24.4.12"></a>24.4.
</ol>
<p>其实这整个过程的链路是很好追踪的,为什么我花了很久呢:</p>
<p><img src="/2024/04/10/read_linux/ecad9f39d64acccafd9bca9958dfafce.jpg" alt="ecad9f39d64acccafd9bca9958dfafce"></p>
<p>因为我想找<code>push_dl_task</code>结果一直在看<code>pull_dl_task</code>也即最终push和pull逻辑是反的,故而我匪夷所思想了半天都没懂hhh……</p>
<p>不过这也让我些许看懂了balance的架构。每个CPU都有一个balance callback的队列,互相之间可以通过这种适时的、异步的调用pull或者push的callback来实现运行队列的负载均衡。我目前水平也不大明白这种架构的好处emmm总之先这么了解一下放在这里吧</p>
<p>因为我想找<code>push_dl_task</code>结果一直在看<code>pull_dl_task</code>(如图所示……这位置实在很星际啊hhh)也即最终push和pull逻辑是反的,故而我匪夷所思想了半天都没懂hhh……</p>
<p>不过这也让我些许看懂了balance的架构。每个CPU都有一个balance callback的队列,互相之间可以通过这种适时的、异步的调用pull或者push的callback来实现任务窃取,从而实现运行队列的负载均衡</p>
<h4 id="per-CPU和Centralized思考-1"><a href="#per-CPU和Centralized思考-1" class="headerlink" title="per-CPU和Centralized思考"></a>per-CPU和Centralized思考</h4><ol>
<li>在单CPU情况下,一开始,CPU位于idle线程,os启动时切换上下文到Bash中。创建线程时,将线程加入该CPU调度队列中。多进程情况下,线程运行时会发生时钟中断,在时钟中断中检测计算时间片,发生上下文切换回idle进程继续reschedule。</li>
<li>在多处理器情况下也是差不多的,一个CPU上运行的线程A创建了另一个线程B,并且线程B优先级更高,那么此时kernel就会可能综合考虑什么负载均衡之类的算法随机挑选出target CPU,将线程B入其队列,然后通过IPI让target CPU进行重新调度。</li>
<li>在多处理器情况下也是差不多的,一个CPU上运行的线程A创建了另一个线程B,并且线程B优先级更高,那么此时kernel就会可能综合考虑什么负载均衡之类的算法挑选出target CPU,将线程B入其队列,然后通过IPI让target CPU进行重新调度。</li>
</ol>
<p>可以很震惊地发现,其实在Linux原生环境中,对于多处理器情况,单纯的per-CPU model为了做到负载均衡,其实已经逐步向centralized model靠拢了,相当于所有CPU都是一个暂时的c位。只不过,这种情况的劣势也很显然,也即我们不得不让工作线程暂停去工作,转而进行这一系列的send IPI(虽然这是异步的),这过程中的各种执行开销,然后估计还要什么获得锁之类的保证同步,感觉开销很大。</p>
<blockquote>
<p>比如说,如果使用ghost,其调用链路只会是</p>
<p>创建线程syscall→<code>fork()</code><code>wake_up_new_task</code><code>sched_class-&gt;task_woken</code>→执行<code>check_preempt_curr</code>→执行<code>__schedule</code>pick出B,抢占A,over</p>
<p>比如说,如果使用ghost,其调用链路会从最坏情况</p>
<p>创建线程syscall→<code>fork()</code><code>wake_up_new_task</code><code>sched_class-&gt;task_woken</code><code>task_woken</code>无事发生,返回→执行<code>__schedule</code>无事发生,B依然在原CPU的数据结构中→执行<code>__balance_callbacks</code>执行<code>push_dl_task</code>,迁移该线程,发送IPI→返回用户态</p>
<p>变成</p>
<p>创建线程syscall→<code>fork()</code>→over</p>
</blockquote>
Expand Down
1 change: 1 addition & 0 deletions about/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ <h4 id="Other"><a href="#Other" class="headerlink" title="Other"></a>Other</h4><
<li><a href="https://xiunianjun.github.io/2023/10/19/open-source-9.19-10.19/">实习经历</a></li>
<li><a href="/2023/06/21/%E8%AF%BE%E7%A8%8B%E5%AD%A6%E4%B9%A0">课程笔记</a></li>
<li><a href="/2024/04/10/unsolved_problems">一些尚未解决的问题…</a></li>
<li><a href="/2024/04/10/read_linux">一些生产开发时对Linux kernel的研究</a></li>
</ul>

</section>
Expand Down
13 changes: 7 additions & 6 deletions search.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,7 @@
<url>/2023/03/13/cmu15445$lab5/</url>
<content><![CDATA[<h1 id="源码解读"><a href="#源码解读" class="headerlink" title="源码解读"></a>源码解读</h1><p><img src="/2023/03/13/cmu15445/image-20231227153858926.png" alt="image-20231227153858926"></p>
<p>对实验中未涉及的parser、binder、planner、storage等部分进行源码的阅读。</p>
<p>以前一直觉得事务是个很抽象的东西,看了bustub实现,才总算窥见了一点真相……我以前觉得事务是一个包含了执行代码的某个执行体,一直很好奇它具体是怎么实现的。现在才发现,事务其实并不算执行体,它只是相当于一个record,对某个执行过程进行一系列记录和维护,比如说维护读写集、维护锁集之类的。这样,当整个执行体结束之后,它就记录了整个执行体的结果,从而可以对其进行复原和释放锁之类的了,相当于是一个统筹全局的爸爸。感觉这也体现了一种解耦思想,将代码的执行和与数据库的交互(读写、锁)解耦,是一个非常伟大的思想。</p>
]]></content>
</entry>
<entry>
Expand Down Expand Up @@ -16544,7 +16545,7 @@ url访问填写<code>http://localhost/webdemo4_war/*.do</code>。</li>
</li>
</ol>
<p>其中,第二种情况稍显复杂,我们可以详细来说说。【下以deadline为例,因为别的比如说RR是非抢占式调度】</p>
<p>首先,全局只有一个调度类对象(不考虑什么多个调度策略,或者说一个线程组只有一个调度类对象,差不多这个意思)。</p>
<p>首先,全局只有一个调度类对象(不考虑什么多个调度策略,或者说一个线程组只有一个调度类对象,差不多这个意思)。也就是说,创建线程会进入该调度类对象中,然后挑选线程也是从该调度类对象中pick。</p>
<p>当一个CPU上运行的线程A创建了另一个线程B,并且假设目前其他CPU跑满了任务,线程B的优先级很高。创建线程的相关syscall最终会调用<code>activate_task</code>【这点暂时存疑】,然后<code>activate_task</code>会调用调度类中的<code>enqueue_task</code>事件,将线程B压入到deadline调度类的红黑树中。<code>activate_task</code>结束返回之后,紧随其后一般会接一个<code>resched_curr</code>,也即将当前CPU reschedule。</p>
<p>这里,就需要分情况讨论了。</p>
<ol>
Expand Down Expand Up @@ -16582,16 +16583,16 @@ url访问填写<code>http://localhost/webdemo4_war/*.do</code>。</li>
</ol>
<p>其实这整个过程的链路是很好追踪的,为什么我花了很久呢:</p>
<p><img src="/2024/04/10/read_linux/ecad9f39d64acccafd9bca9958dfafce.jpg" alt="ecad9f39d64acccafd9bca9958dfafce"></p>
<p>因为我想找<code>push_dl_task</code>结果一直在看<code>pull_dl_task</code>也即最终push和pull逻辑是反的,故而我匪夷所思想了半天都没懂hhh……</p>
<p>不过这也让我些许看懂了balance的架构。每个CPU都有一个balance callback的队列,互相之间可以通过这种适时的、异步的调用pull或者push的callback来实现运行队列的负载均衡。我目前水平也不大明白这种架构的好处emmm总之先这么了解一下放在这里吧。</p>
<p>因为我想找<code>push_dl_task</code>结果一直在看<code>pull_dl_task</code>(如图所示……这位置实在很星际啊hhh)也即最终push和pull逻辑是反的,故而我匪夷所思想了半天都没懂hhh……</p>
<p>不过这也让我些许看懂了balance的架构。每个CPU都有一个balance callback的队列,互相之间可以通过这种适时的、异步的调用pull或者push的callback来实现任务窃取,从而实现运行队列的负载均衡。</p>
<h4 id="per-CPU和Centralized思考-1"><a href="#per-CPU和Centralized思考-1" class="headerlink" title="per-CPU和Centralized思考"></a>per-CPU和Centralized思考</h4><ol>
<li>在单CPU情况下,一开始,CPU位于idle线程,os启动时切换上下文到Bash中。创建线程时,将线程加入该CPU调度队列中。多进程情况下,线程运行时会发生时钟中断,在时钟中断中检测计算时间片,发生上下文切换回idle进程继续reschedule。</li>
<li>在多处理器情况下也是差不多的,一个CPU上运行的线程A创建了另一个线程B,并且线程B优先级更高,那么此时kernel就会可能综合考虑什么负载均衡之类的算法随机挑选出target CPU,将线程B入其队列,然后通过IPI让target CPU进行重新调度。</li>
<li>在多处理器情况下也是差不多的,一个CPU上运行的线程A创建了另一个线程B,并且线程B优先级更高,那么此时kernel就会可能综合考虑什么负载均衡之类的算法挑选出target CPU,将线程B入其队列,然后通过IPI让target CPU进行重新调度。</li>
</ol>
<p>可以很震惊地发现,其实在Linux原生环境中,对于多处理器情况,单纯的per-CPU model为了做到负载均衡,其实已经逐步向centralized model靠拢了,相当于所有CPU都是一个暂时的c位。只不过,这种情况的劣势也很显然,也即我们不得不让工作线程暂停去工作,转而进行这一系列的send IPI(虽然这是异步的),这过程中的各种执行开销,然后估计还要什么获得锁之类的保证同步,感觉开销很大。</p>
<blockquote>
<p>比如说,如果使用ghost,其调用链路只会是:</p>
<p>创建线程syscall→<code>fork()</code>→<code>wake_up_new_task</code>→<code>sched_class-&gt;task_woken</code>→执行<code>check_preempt_curr</code>→执行<code>__schedule</code>→pick出B,抢占A,over</p>
<p>比如说,如果使用ghost,其调用链路会从最坏情况:</p>
<p>创建线程syscall→<code>fork()</code>→<code>wake_up_new_task</code>→<code>sched_class-&gt;task_woken</code>,<code>task_woken</code>无事发生,返回→执行<code>__schedule</code>→无事发生,B依然在原CPU的数据结构中→执行<code>__balance_callbacks</code>→执行<code>push_dl_task</code>,迁移该线程,发送IPI→返回用户态</p>
<p>变成</p>
<p>创建线程syscall→<code>fork()</code>→over</p>
</blockquote>
Expand Down

0 comments on commit 707a920

Please sign in to comment.