generated from fastai/fast_template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
213 lines (99 loc) · 221 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>OS-Lab4-Challenge</title>
<link href="/2023/06/30/OS-Lab4-Challenge/"/>
<url>/2023/06/30/OS-Lab4-Challenge/</url>
<content type="html"><![CDATA[<h1 id="lab4-challenge"><a href="#lab4-challenge" class="headerlink" title="lab4 challenge"></a>lab4 challenge</h1><h2 id="任务背景"><a href="#任务背景" class="headerlink" title="任务背景"></a>任务背景</h2><p>信号(英语:Signal)是 Unix、类 Unix 以及其他 POSIX 兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统会打断进程正常的控制流程,此时,任何非原子操作都将被打断。如果进程注册了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。信号的机制类似于硬件中断(异常),不同之处在于中断由处理器发出并由内核处理,而信号由内核发出并由用户程序处理。除了进程通过系统调用向另一进程(或它自身)发出的信号,内核还可以将发生的中断通过信号通知给引发中断的进程。如果说系统调用是一种用户程序通知内核的机制,那么信号就是内核通知用户程序的机制。</p><p>你的任务是在我们的 MOS 操作系统中实现这样一套机制。</p><h2 id="任务描述"><a href="#任务描述" class="headerlink" title="任务描述"></a>任务描述</h2><p>本挑战性任务总共包含三个部分的内容,需要支持信号的<strong>注册</strong>、<strong>发送</strong>和<strong>处理</strong>。</p><h3 id="信号的注册"><a href="#信号的注册" class="headerlink" title="信号的注册"></a>信号的注册</h3><p>信号的注册函数采用下面的函数:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);</span><br></pre></td></tr></table></figure><p>其中 <code>signum</code> 表示需要处理信号的编号,<code>act</code> 表示新的信号处理结构体,旧的信号处理结构体则需要在 <code>oldact != NULL</code> 时保存该指针在对应的地址空间中。</p><p>为了简化信号机制,你只需要考虑 <code>signum</code> 小于或等于 <code>64</code> 的情况,当收到编号大于64的信号时直接返回异常码 <code>-1</code>。</p><p><code>sigaction</code> 结构体包含下面两个部分:</p><figure class="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></pre></td><td class="code"><pre><span class="line">struct sigaction{</span><br><span class="line"> void (*sa_handler)(int);</span><br><span class="line"> sigset_t sa_mask;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>其中 <code>sa_handler</code> 表示信号处理函数,<code>sa_mask</code> 表示在<strong>运行信号处理函数过程中</strong>的信号掩码,其结构体如下所示:</p><figure class="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></pre></td><td class="code"><pre><span class="line">struct sigset_t{</span><br><span class="line"> int sig[2]; //最多 32*2=64 种信号</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>信号的屏蔽是指当进程在收到信号时,不触发该信号的处理函数,而是等到信号屏蔽结束后才处理该信号。<code>sigset_t</code>的每一位(bit)表示一种信号的屏蔽掩码,当其为 <code>0</code> 时不屏蔽该信号,当其为 <code>1</code> 时则屏蔽该信号直到其位置被修改为 <code>0</code>。</p><p>你需要实现下面的函数来对<strong>进程的信号掩码</strong>进行修改:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);</span><br></pre></td></tr></table></figure><p>其中 <code>how</code> 表明对于信号掩码的修改类型方式,包括下面三种方式:</p><ul><li><code>SIG_BLOCK</code>(<code>how</code> 为 <code>0</code>): 将 <code>set</code> 参数中指定的信号添加到当前进程的信号掩码中</li><li><code>SIG_UNBLOCK</code>(<code>how</code> 为 <code>1</code>): 将 <code>set</code> 参数中指定的信号从当前进程的信号掩码中删除</li><li><code>SIG_SETMASK</code>(<code>how</code> 为 <code>2</code>): 将当前进程的信号掩码设置为 <code>set</code> 参数中指定的信号集</li></ul><p>当 <code>oldset</code> 不为 <code>NULL</code> 时,还需将原有的信号掩码放在 <code>oldset</code> 指定的地址空间中。</p><p>正常执行则返回 <code>0</code>,否则返回异常码 <code>-1</code>.</p><p>为了方便对于信号集操作,你需要实现下面五个函数:</p><figure class="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></pre></td><td class="code"><pre><span class="line">void sigemptyset(sigset_t *set); // 清空信号集,将所有位都设置为 0</span><br><span class="line">void sigfillset(sigset_t *set); // 设置信号集,即将所有位都设置为 1</span><br><span class="line">void sigaddset(sigset_t *set, int signum); // 向信号集中添加一个信号,即将指定信号的位设置为 1</span><br><span class="line">void sigdelset(sigset_t *set, int signum); // 从信号集中删除一个信号,即将指定信号的位设置为 0</span><br><span class="line">int sigismember(const sigset_t *set, int signum); // 检查一个信号是否在信号集中,如果在则返回 1,否则返回 0</span><br></pre></td></tr></table></figure><h3 id="信号的发送"><a href="#信号的发送" class="headerlink" title="信号的发送"></a>信号的发送</h3><p>信号的发送采用下面的函数:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">int kill(u_int envid, int sig);</span><br></pre></td></tr></table></figure><p>向进程控制号编号为 <code>envid</code> 的进程发送 <code>sig</code> 信号,注意当 <code>envid = 0</code> 时代表向自身发送信号。该函数成为完成返回 <code>0</code>,如果信号编号超过限制或者进程编号不存在则返回 <code>-1</code>。</p><h3 id="信号的处理"><a href="#信号的处理" class="headerlink" title="信号的处理"></a>信号的处理</h3><p>当进程被调度在之后继续运行时,首先需要处理自身收到的所有信号,对于后面来的信号,认为其优先级更高而先进行处理。对于被阻塞信号,<strong>保留</strong>其信号信息。对于未阻塞信号,则需要跳转到信号处理函数,如果未注册处理函数,则按照默认的处理动作进行。</p><p>本挑战性任务只考虑 <code>64</code> 种信号,其编号为从 <code>1</code> 到 <code>64</code>,每一个信号都对应信号掩码的一位。</p><table><thead><tr><th align="left">信号</th><th align="left">编号</th><th align="left">来源</th><th align="left">描述</th><th align="left">默认处理动作</th></tr></thead><tbody><tr><td align="left"><code>SIGKILL</code></td><td align="left">9</td><td align="left">操作系统</td><td align="left">强制结束程序的运行(该信号不能被阻塞)</td><td align="left">结束程序的运行</td></tr><tr><td align="left"><code>SIGSEGV</code></td><td align="left">11</td><td align="left">操作系统</td><td align="left">用户程序访问了页表中未映射且地址严格小于 <code>0x3FE000</code> 的虚拟页</td><td align="left">结束程序的运行</td></tr><tr><td align="left"><code>SIGTERM</code></td><td align="left">15</td><td align="left">用户程序</td><td align="left">用于终止进程,但允许目标进程通信号处理函数拦截</td><td align="left">结束程序的运行</td></tr></tbody></table><p>其余类型信号默认处理动作为<strong>忽略</strong>。</p><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><ul><li>上述所说的函数声明均为用户态函数,部分函数需要通过系统调用陷入内核态完成修改</li><li>注意 <code>SIGSEGV</code> 信号的处理,不应该在内核态 panic</li><li>多个信号的处理需要实现信号重入机制,注意用户上下文的保存</li><li>fork 后子进程应继承父进程的信号处理函数</li><li>注意将内核态的内容拷贝到用户态时,需要考虑该地址空间存在写时复制的问题</li><li>注意信号的编号为从1开始的下标编码</li></ul><h2 id="简单的测试程序"><a href="#简单的测试程序" class="headerlink" title="简单的测试程序"></a>简单的测试程序</h2><p>通过以下测试程序不能确保你的实现是正确的,请构造更多的测试程序。</p><h3 id="基本信号测试"><a href="#基本信号测试" class="headerlink" title="基本信号测试"></a>基本信号测试</h3><h4 id="测试代码"><a href="#测试代码" class="headerlink" title="测试代码"></a>测试代码</h4><figure class="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></pre></td><td class="code"><pre><span class="line">#include <lib.h></span><br><span class="line"></span><br><span class="line">int global = 0;</span><br><span class="line">void handler(int num) {</span><br><span class="line"> debugf("Reach handler, now the signum is %d!\n", num);</span><br><span class="line"> global = 1;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">#define TEST_NUM 2</span><br><span class="line">int main(int argc, char **argv) {</span><br><span class="line"> sigset_t set;</span><br><span class="line"> sigemptyset(&set);</span><br><span class="line"> struct sigaction sig;</span><br><span class="line"> sig.sa_handler = handler;</span><br><span class="line"> sig.sa_mask = set;</span><br><span class="line"> panic_on(sigaction(TEST_NUM, &sig, NULL));</span><br><span class="line"> sigaddset(&set, TEST_NUM);</span><br><span class="line"> panic_on(sigprocmask(0, &set, NULL));</span><br><span class="line"> kill(0, TEST_NUM);</span><br><span class="line"> int ans = 0;</span><br><span class="line"> for (int i = 0; i < 10000000; i++) {</span><br><span class="line"> ans += i;</span><br><span class="line"> }</span><br><span class="line"> panic_on(sigprocmask(1, &set, NULL));</span><br><span class="line"> debugf("global = %d.\n", global);</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="正确输出"><a href="#正确输出" class="headerlink" title="正确输出"></a>正确输出</h4><figure class="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">Reach handler, now the signum is 2!</span><br><span class="line">global = 1.</span><br></pre></td></tr></table></figure><h3 id="空指针测试"><a href="#空指针测试" class="headerlink" title="空指针测试"></a>空指针测试</h3><h4 id="测试代码-1"><a href="#测试代码-1" class="headerlink" title="测试代码"></a>测试代码</h4><figure class="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></pre></td><td class="code"><pre><span class="line">#include <lib.h></span><br><span class="line"></span><br><span class="line">int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};</span><br><span class="line">int *test = NULL;</span><br><span class="line">void sgv_handler(int num) {</span><br><span class="line"> debugf("Segment fault appear!\n");</span><br><span class="line"> test = &a[0];</span><br><span class="line"> debugf("test = %d.\n", *test);</span><br><span class="line"> exit();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">int main(int argc, char **argv) {</span><br><span class="line"> sigset_t set;</span><br><span class="line"> sigemptyset(&set);</span><br><span class="line"> struct sigaction sig;</span><br><span class="line"> sig.sa_handler = sgv_handler;</span><br><span class="line"> sig.sa_mask = set;</span><br><span class="line"> panic_on(sigaction(11, &sig, NULL));</span><br><span class="line"> *test = 10;</span><br><span class="line"> debugf("test = %d.\n", *test);</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="正确输出-1"><a href="#正确输出-1" class="headerlink" title="正确输出"></a>正确输出</h4><figure class="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">Segment fault appear!</span><br><span class="line">test = 1.</span><br></pre></td></tr></table></figure><h3 id="写时复制测试"><a href="#写时复制测试" class="headerlink" title="写时复制测试"></a>写时复制测试</h3><h4 id="测试代码-2"><a href="#测试代码-2" class="headerlink" title="测试代码"></a>测试代码</h4><figure class="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></pre></td><td class="code"><pre><span class="line">#include <lib.h></span><br><span class="line"></span><br><span class="line">sigset_t set2;</span><br><span class="line"></span><br><span class="line">int main(int argc, char **argv) {</span><br><span class="line"> sigset_t set;</span><br><span class="line"> sigemptyset(&set);</span><br><span class="line"> sigaddset(&set, 1);</span><br><span class="line"> sigaddset(&set, 2);</span><br><span class="line"> panic_on(sigprocmask(0, &set, NULL));</span><br><span class="line"> sigdelset(&set, 2);</span><br><span class="line"> int ret = fork();</span><br><span class="line"> if (ret != 0) {</span><br><span class="line"> panic_on(sigprocmask(0, &set2, &set));</span><br><span class="line"> debugf("Father: %d.\n", sigismember(&set, 2));</span><br><span class="line"> } else {</span><br><span class="line"> debugf("Child: %d.\n", sigismember(&set, 2));</span><br><span class="line"> }</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="正确输出-2"><a href="#正确输出-2" class="headerlink" title="正确输出"></a>正确输出</h4><figure class="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">Father: 1.</span><br><span class="line">Child: 0.</span><br></pre></td></tr></table></figure><h2 id="任务要求"><a href="#任务要求" class="headerlink" title="任务要求"></a>任务要求</h2><ul><li><p>你需要给出你的信号系统设计,报告各个信号结构体所在位置</p></li><li><p>对于各种类型的信号和使用场景,你应该给予充分的测试</p></li><li><p>请在lab6完成的基础上</p><p>,基于lab6分支,自行建立</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lab4-challenge</span><br></pre></td></tr></table></figure><p>分支,在该分支完成代码后,push 到个人的远程仓库。代码内需要包含对于功能的详细测试程序,测试程序本身及运行测试程序得到的运行结果应具有足够的可读性。</p><figure class="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">git checkout lab6</span><br><span class="line">git add .</span><br><span class="line">git commit -m "xxxxx"</span><br><span class="line">git checkout -b lab4-challenge</span><br><span class="line"># 完成代码</span><br><span class="line">git push origin lab4-challenge:lab4-challenge</span><br></pre></td></tr></table></figure></li><li><p>实验报告请提交至spoc系统,在书写实验报告和准备申优答辩时,请加入以下内容:</p></li><li><p>对于任务的实现思路,并配合关键代码进行说明。</p></li><li><p>对于功能的详细测试程序,以及运行测试程序得到的运行结果。</p></li><li><p>完成挑战性任务过程中遇到的问题及解决方案。</p></li></ul><h2 id="1、你需要给出你的信号系统设计,报告各个信号结构体所在位置"><a href="#1、你需要给出你的信号系统设计,报告各个信号结构体所在位置" class="headerlink" title="1、你需要给出你的信号系统设计,报告各个信号结构体所在位置"></a>1、你需要给出你的信号系统设计,报告各个信号结构体所在位置</h2><p>/include/mysignal.h</p><figure class="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="meta"># <span class="keyword">ifndef</span> MYSIGNAL_H</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> MYSIGNAL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX_SIGNALS 64</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIG_BLOCK 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIG_UNBLOCK 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIG_SETMASK 2</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGKILL 9</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGSEGV 11</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIGTERM 15</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX_SIGNALS 64</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">sigset_t</span>{</span></span><br><span class="line"> <span class="type">int</span> sig[MAX_SIGNALS / <span class="number">32</span>];</span><br><span class="line">} <span class="type">sigset_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span>{</span></span><br><span class="line"> <span class="type">void</span> (*sa_handler)(<span class="type">int</span>);</span><br><span class="line"> <span class="type">sigset_t</span> sa_mask;</span><br><span class="line">};</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">signal</span>{</span></span><br><span class="line"> <span class="type">int</span> signum;</span><br><span class="line"> TAILQ_ENTRY(signal) sig_wait_link;</span><br><span class="line"> LIST_ENTRY(signal) sig_free_link;</span><br><span class="line">};</span><br><span class="line">TAILQ_HEAD(Sig_wait_list,signal);</span><br><span class="line">LIST_HEAD(Sig_free_list,signal);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>/user/mysignal.c</p><figure class="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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><env.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><lib.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="type">int</span> <span class="title function_">sigaction</span><span class="params">(<span class="type">int</span> signum, <span class="type">const</span> <span class="keyword">struct</span> sigaction *act, <span class="keyword">struct</span> sigaction *oldact)</span> {</span><br><span class="line"> <span class="keyword">return</span> syscall_sigaction(signum,act,oldact);</span><br><span class="line">}</span><br><span class="line"><span class="type">int</span> <span class="title function_">sigprocmask</span><span class="params">(<span class="type">int</span> how, <span class="type">const</span> <span class="type">sigset_t</span> *<span class="built_in">set</span>, <span class="type">sigset_t</span> *oldset)</span> {</span><br><span class="line"> <span class="keyword">return</span> syscall_sigprocmask(how,<span class="built_in">set</span>,oldset);</span><br><span class="line">}</span><br><span class="line"><span class="type">void</span> <span class="title function_">sigemptyset</span><span class="params">(<span class="type">sigset_t</span> *<span class="built_in">set</span>)</span> {</span><br><span class="line"> <span class="comment">// 将信号集中的所有位都设置为 0</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < MAX_SIGNALS / <span class="number">32</span>; i++) {</span><br><span class="line"> <span class="built_in">set</span>->sig[i] = <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="type">void</span> <span class="title function_">sigfillset</span><span class="params">(<span class="type">sigset_t</span> *<span class="built_in">set</span>)</span> {</span><br><span class="line"> <span class="comment">// 将信号集中的所有位都设置为 1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < MAX_SIGNALS / <span class="number">32</span>; i++) {</span><br><span class="line"> <span class="built_in">set</span>->sig[i] = ~<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="type">void</span> <span class="title function_">sigaddset</span><span class="params">(<span class="type">sigset_t</span> *<span class="built_in">set</span>, <span class="type">int</span> signum)</span> {</span><br><span class="line"> <span class="comment">// 向信号集中添加一个信号,即将指定信号的位设置为 1</span></span><br><span class="line"> <span class="keyword">if</span> (signum > <span class="number">0</span> && signum <= MAX_SIGNALS) {</span><br><span class="line"> <span class="type">int</span> index = (signum - <span class="number">1</span>) / <span class="number">32</span>;</span><br><span class="line"> <span class="type">int</span> bit = (signum - <span class="number">1</span>) % <span class="number">32</span>;</span><br><span class="line"> <span class="built_in">set</span>->sig[index] |= (<span class="number">1</span> << bit);</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_">sigdelset</span><span class="params">(<span class="type">sigset_t</span> *<span class="built_in">set</span>, <span class="type">int</span> signum)</span> {</span><br><span class="line"> <span class="comment">// 从信号集中删除一个信号,即将指定信号的位设置为 0</span></span><br><span class="line"> <span class="keyword">if</span> (signum > <span class="number">0</span> && signum <= MAX_SIGNALS) {</span><br><span class="line"> <span class="type">int</span> index = (signum - <span class="number">1</span>) / <span class="number">32</span>;</span><br><span class="line"> <span class="type">int</span> bit = (signum - <span class="number">1</span>) % <span class="number">32</span>;</span><br><span class="line"> <span class="built_in">set</span>->sig[index] &= ~(<span class="number">1</span> << bit);</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_">sigismember</span><span class="params">(<span class="type">const</span> <span class="type">sigset_t</span> *<span class="built_in">set</span>, <span class="type">int</span> signum)</span> {</span><br><span class="line"> <span class="comment">// 检查一个信号是否在信号集中,如果在则返回 1,否则返回 0</span></span><br><span class="line"> <span class="keyword">if</span> (signum > <span class="number">0</span> && signum <= MAX_SIGNALS) {</span><br><span class="line"> <span class="type">int</span> index = (signum - <span class="number">1</span>) / <span class="number">32</span>;</span><br><span class="line"> <span class="type">int</span> bit = (signum - <span class="number">1</span>) % <span class="number">32</span>;</span><br><span class="line"> <span class="keyword">return</span> (<span class="built_in">set</span>->sig[index] & (<span class="number">1</span> << bit)) != <span class="number">0</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 class="type">int</span> <span class="title function_">kill</span><span class="params">(u_int envid, <span class="type">int</span> sig)</span> {</span><br><span class="line"> <span class="keyword">return</span> syscall_kill(envid,sig);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="2、对于任务的实现思路,并配合关键代码进行说明。"><a href="#2、对于任务的实现思路,并配合关键代码进行说明。" class="headerlink" title="2、对于任务的实现思路,并配合关键代码进行说明。"></a>2、对于任务的实现思路,并配合关键代码进行说明。</h2><p>如下图所示,标号处对应代码</p><img src="/2023/06/30/OS-Lab4-Challenge/1.png" class title="This is an example image"><ul><li>1/2:</li></ul><figure class="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="comment">//user/lib/mysignal.c:</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">kill</span><span class="params">(u_int envid, <span class="type">int</span> sig)</span> {</span><br><span class="line"> <span class="keyword">return</span> syscall_kill(envid,sig);</span><br><span class="line">}</span><br><span class="line"><span class="comment">//kern/syscall_all.c:</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">sys_kill</span><span class="params">(u_int envid, <span class="type">int</span> sig)</span> {</span><br><span class="line"> <span class="comment">// 检查信号编号是否超过限制</span></span><br><span class="line"> <span class="keyword">if</span> (sig > MAX_SIGNALS || sig <= <span class="number">0</span>) {</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 class="class"><span class="keyword">struct</span> <span class="title">Env</span> *<span class="title">env</span>;</span></span><br><span class="line"> <span class="keyword">if</span> (envid2env(envid, &env, <span class="number">0</span>)!=<span class="number">0</span>) {</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 class="keyword">if</span>(LIST_EMPTY(&sig_free_list)){</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 class="class"><span class="keyword">struct</span> <span class="title">signal</span> *<span class="title">signal</span>=</span>LIST_FIRST(&sig_free_list);</span><br><span class="line"> LIST_REMOVE(signal,sig_free_link);</span><br><span class="line"> <span class="built_in">memset</span>(signal,<span class="number">0</span>,<span class="keyword">sizeof</span>(<span class="keyword">struct</span> signal));</span><br><span class="line"> signal->signum=sig;</span><br><span class="line"> TAILQ_INSERT_HEAD(&env->sig_wait_list,signal,sig_wait_link);<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><ul><li>3/4:</li></ul><figure class="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">//kern/genex.S:</span></span><br><span class="line">.macro BUILD_HANDLER exception handler</span><br><span class="line"><span class="title function_">NESTED</span><span class="params">(handle_\exception, TF_SIZE + <span class="number">8</span>, zero)</span></span><br><span class="line">move a0, sp</span><br><span class="line">addiu sp, sp, -8</span><br><span class="line">jal \handler</span><br><span class="line">addiu sp, sp, 8</span><br><span class="line">j ret_from_exception</span><br><span class="line"><span class="title function_">END</span><span class="params">(handle_\exception)</span></span><br><span class="line">.endm</span><br><span class="line"></span><br><span class="line">.text</span><br><span class="line"></span><br><span class="line"><span class="title function_">NESTED</span><span class="params">(handle_int, TF_SIZE, zero)</span></span><br><span class="line">mfc0 t0, CP0_CAUSE</span><br><span class="line">mfc0 t2, CP0_STATUS</span><br><span class="line">and t0, t2</span><br><span class="line">andi t1, t0, STATUS_IM4</span><br><span class="line">bnez t1, timer_irq</span><br><span class="line"><span class="comment">// <span class="doctag">TODO:</span> handle other irqs</span></span><br><span class="line">timer_irq:</span><br><span class="line">sw zero, <span class="params">(KSEG1 | DEV_RTC_ADDRESS | DEV_RTC_INTERRUPT_ACK)</span></span><br><span class="line">li a0, 0</span><br><span class="line">j schedule</span><br><span class="line"><span class="title function_">END</span><span class="params">(handle_int)</span></span><br><span class="line"></span><br><span class="line">BUILD_HANDLER tlb do_tlb_refill</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !defined(LAB) || LAB >= 4</span></span><br><span class="line">BUILD_HANDLER mod do_tlb_mod</span><br><span class="line">BUILD_HANDLER sys do_syscall </span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">BUILD_HANDLER reserved do_reserved</span><br></pre></td></tr></table></figure><ul><li>5/6:</li></ul><figure class="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">//kern/genex.S:</span></span><br><span class="line">FEXPORT(ret_from_exception)</span><br><span class="line">move a0, sp # 给do_siganl传入参数</span><br><span class="line">addiu sp, sp, <span class="number">-8</span></span><br><span class="line">nop</span><br><span class="line">jal do_signal<span class="comment">//到这进入信号处理</span></span><br><span class="line">nop</span><br><span class="line">addiu sp, sp, <span class="number">8</span></span><br><span class="line">RESTORE_SOME</span><br><span class="line">lw k0, TF_EPC(sp)</span><br><span class="line">lw sp, TF_REG29(sp) <span class="comment">/* Deallocate stack */</span></span><br><span class="line">.<span class="built_in">set</span> noreorder</span><br><span class="line">jr k0</span><br><span class="line">rfe</span><br><span class="line">.<span class="built_in">set</span> reorder</span><br></pre></td></tr></table></figure><ul><li>7/8:</li></ul><figure class="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 class="comment">//kern/syscall_all.c</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">do_signal</span><span class="params">(<span class="keyword">struct</span> Trapframe *tf)</span>{</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">signal</span> *<span class="title">signal</span>;</span></span><br><span class="line"> TAILQ_FOREACH(signal,&curenv->sig_wait_list,sig_wait_link){</span><br><span class="line"> <span class="keyword">if</span>(signal->signum>=<span class="number">1</span>&&signal->signum<=<span class="number">64</span>){</span><br><span class="line"> <span class="keyword">if</span>(signal->signum==SIGSEGV||signal->signum==SIGKILL){</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(curenv->top><span class="number">-1</span>){ <span class="comment">//表明信号重入中</span></span><br><span class="line"> u_int signum=curenv->running_sig[curenv->top]<span class="number">-1</span>;</span><br><span class="line"><span class="type">int</span> index = (signal->signum - <span class="number">1</span>) / <span class="number">32</span>;</span><br><span class="line"> <span class="type">int</span> bit = (signal->signum - <span class="number">1</span>) % <span class="number">32</span>;</span><br><span class="line"> <span class="keyword">if</span>((curenv->env_sigaction[signum].sa_mask.sig[index] & (<span class="number">1</span> << bit)) != <span class="number">0</span>){</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> } </span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="type">int</span> index = (signal->signum - <span class="number">1</span>) / <span class="number">32</span>;</span><br><span class="line"> <span class="type">int</span> bit = (signal->signum - <span class="number">1</span>) % <span class="number">32</span>;</span><br><span class="line"> <span class="keyword">if</span>((curenv->env_signal_mask.sig[index] & (<span class="number">1</span> << bit)) != <span class="number">0</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"> TAILQ_REMOVE(&curenv->sig_wait_list,signal,sig_wait_link);</span><br><span class="line"><span class="comment">//完成一个信号的初步处理</span></span><br><span class="line"> signal_handle(signal->signum<span class="number">-1</span>,tf);</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><br><span class="line"><span class="type">void</span> <span class="title function_">signal_handle</span><span class="params">(<span class="type">int</span> num,<span class="keyword">struct</span> Trapframe *tf)</span> {</span><br><span class="line"> <span class="keyword">if</span> (curenv->env_sigaction[num].sa_handler||num==SIGSEGV||num==SIGKILL||num==SIGTERM) {</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Trapframe</span> <span class="title">tmp</span> =</span> *tf;</span><br><span class="line"> tf->regs[<span class="number">29</span>] -= <span class="keyword">sizeof</span>(<span class="keyword">struct</span> Trapframe);</span><br><span class="line"> *(<span class="keyword">struct</span> Trapframe *)tf->regs[<span class="number">29</span>] = tmp;</span><br><span class="line"> <span class="keyword">if</span>(curenv->env_signal_caller){</span><br><span class="line"> tf->regs[<span class="number">4</span>] = tf->regs[<span class="number">29</span>];<span class="comment">//$a0</span></span><br><span class="line"> tf->regs[<span class="number">5</span>] = num;<span class="comment">//$a1</span></span><br><span class="line"> tf->regs[<span class="number">6</span>] = curenv->env_sigaction[num].sa_handler;<span class="comment">//$a2</span></span><br><span class="line"> tf->regs[<span class="number">29</span>] -= <span class="number">3</span>*<span class="keyword">sizeof</span>(tf->regs[<span class="number">4</span>]); </span><br><span class="line"> sys_push_running_sig(num+<span class="number">1</span>);</span><br><span class="line"> tf->cp0_epc = curenv->env_signal_caller;<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></pre></td></tr></table></figure><ul><li>9:</li></ul><figure class="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">//kern/genex.S:</span></span><br><span class="line">FEXPORT(ret_from_exception)</span><br><span class="line">move a0, sp # 给do_siganl传入参数</span><br><span class="line">addiu sp, sp, <span class="number">-8</span></span><br><span class="line">nop</span><br><span class="line">jal do_signal<span class="comment">//从这返回</span></span><br><span class="line">nop</span><br><span class="line">addiu sp, sp, <span class="number">8</span></span><br><span class="line">RESTORE_SOME</span><br><span class="line">lw k0, TF_EPC(sp)</span><br><span class="line">lw sp, TF_REG29(sp) <span class="comment">/* Deallocate stack */</span></span><br><span class="line">.<span class="built_in">set</span> noreorder</span><br><span class="line">jr k0<span class="comment">//跳到EPC,没有信号机制的时候这是导致异常的指令,现在是把这条指令向下压栈,现在指向的是统一的env_signal_caller,可能有好多个叠在原EPC之上</span></span><br><span class="line">rfe</span><br><span class="line">.<span class="built_in">set</span> reorder</span><br></pre></td></tr></table></figure><ul><li>10/11/12:</li></ul><figure class="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">static</span> <span class="type">void</span> __attribute__((<span class="keyword">noreturn</span>)) signal_caller(<span class="keyword">struct</span> Trapframe *tf,<span class="type">int</span> num,<span class="type">void</span> (*sa_handler)(<span class="type">int</span>)) {</span><br><span class="line"> <span class="keyword">if</span>(sa_handler){</span><br><span class="line"> <span class="type">void</span> (*func)(<span class="type">int</span>);</span><br><span class="line"> func=sa_handler;</span><br><span class="line"> func(num);</span><br><span class="line"> syscall_pop_running_sig();</span><br><span class="line"> syscall_set_trapframe(<span class="number">0</span>,tf);</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(num==SIGSEGV||num==SIGKILL||num==SIGTERM){</span><br><span class="line"> syscall_pop_running_sig();</span><br><span class="line"> <span class="built_in">exit</span>();<span class="comment">//结束进程</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><code>SIGSEGV</code> 信号的处理</li></ul><figure class="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">//kern/tlbex.c</span></span><br><span class="line"><span class="keyword">if</span> (va < UTEMP) {</span><br><span class="line">sys_kill(<span class="number">0</span>,<span class="number">11</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>fork 后子进程应继承父进程的信号处理函数,注意将内核态的内容拷贝到用户态时,需要考虑该地址空间存在写时复制的问题</li></ul><figure class="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">//kern/syscall_all.c</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">sys_exofork</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="comment">//...............................</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<MAX_SIGNALS;i++){ <span class="comment">// for signal</span></span><br><span class="line"> e->env_sigaction[i]=curenv->env_sigaction[i];</span><br><span class="line"> e->running_sig[i]=<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line">e->env_signal_caller=curenv->env_signal_caller; <span class="comment">//继承handler</span></span><br><span class="line"><span class="keyword">return</span> e->env_id;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="3、对于功能的详细测试程序,以及运行测试程序得到的运行结果。"><a href="#3、对于功能的详细测试程序,以及运行测试程序得到的运行结果。" class="headerlink" title="3、对于功能的详细测试程序,以及运行测试程序得到的运行结果。"></a>3、对于功能的详细测试程序,以及运行测试程序得到的运行结果。</h2><ul><li>kill_test</li></ul><figure class="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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><lib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TEST_NUM 10</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">OK1!</span></span><br><span class="line"><span class="comment">OK12!</span></span><br><span class="line"><span class="comment">OK23!</span></span><br><span class="line"><span class="comment">OK34!</span></span><br><span class="line"><span class="comment">OK45!</span></span><br><span class="line"><span class="comment">OK56!</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">handler</span><span class="params">(<span class="type">int</span> num)</span>{</span><br><span class="line"> debugf(<span class="string">"OK%d!\n"</span>,num);</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="type">sigset_t</span> <span class="built_in">set</span>;</span><br><span class="line"> sigemptyset(&<span class="built_in">set</span>);</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">sig</span>;</span></span><br><span class="line"> sig.sa_handler=handler;</span><br><span class="line"> sig.sa_mask=<span class="built_in">set</span>;</span><br><span class="line"> sigaction(<span class="number">1</span>, &sig, <span class="literal">NULL</span>);</span><br><span class="line"> sigaction(<span class="number">12</span>, &sig, <span class="literal">NULL</span>);</span><br><span class="line"> sigaction(<span class="number">23</span>, &sig, <span class="literal">NULL</span>);</span><br><span class="line"> sigaction(<span class="number">34</span>, &sig, <span class="literal">NULL</span>);</span><br><span class="line"> sigaction(<span class="number">45</span>, &sig, <span class="literal">NULL</span>);</span><br><span class="line"> sigaction(<span class="number">56</span>, &sig, <span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">if</span>(kill(<span class="number">0</span>,<span class="number">1</span>)<<span class="number">0</span>){</span><br><span class="line"> user_panic(<span class="string">"wrong in kill_1\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(kill(<span class="number">0</span>,<span class="number">12</span>)<<span class="number">0</span>){</span><br><span class="line"> user_panic(<span class="string">"wrong in kill_2\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(kill(<span class="number">0</span>,<span class="number">23</span>)<<span class="number">0</span>){</span><br><span class="line"> user_panic(<span class="string">"wrong in kill_3\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(kill(<span class="number">0</span>,<span class="number">34</span>)<<span class="number">0</span>){</span><br><span class="line"> user_panic(<span class="string">"wrong in kill_4\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(kill(<span class="number">0</span>,<span class="number">45</span>)<<span class="number">0</span>){</span><br><span class="line"> user_panic(<span class="string">"wrong in kill_5\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(kill(<span class="number">0</span>,<span class="number">56</span>)<<span class="number">0</span>){</span><br><span class="line"> user_panic(<span class="string">"wrong in kill_6\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(kill(<span class="number">0</span>,<span class="number">1000</span>)!=<span class="number">-1</span>){ <span class="comment">//大于64</span></span><br><span class="line"> user_panic(<span class="string">"wrong in kill_7\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//getwaitlist(0);</span></span><br><span class="line"> <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">10000000</span>; i++) {</span><br><span class="line"> ans += i;</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><ul><li>cow_test</li></ul><figure class="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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><lib.h></span></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">Father: 1.</span></span><br><span class="line"><span class="comment">I can see envid 4097.</span></span><br><span class="line"><span class="comment">Child: 0.</span></span><br><span class="line"><span class="comment">I can see envid 0.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">handler</span><span class="params">(<span class="type">int</span> num)</span>{</span><br><span class="line"> debugf(<span class="string">"I can see envid %d.\n"</span>,num);</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">int</span> argc, <span class="type">char</span> **argv)</span> {</span><br><span class="line"> <span class="type">sigset_t</span> <span class="built_in">set</span>;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">sig</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">swap_sig</span>;</span></span><br><span class="line"> sig.sa_handler=handler;</span><br><span class="line"> sigemptyset(&<span class="built_in">set</span>);</span><br><span class="line"> sigaddset(&<span class="built_in">set</span>, <span class="number">1</span>);</span><br><span class="line"> sigaddset(&<span class="built_in">set</span>, <span class="number">2</span>);</span><br><span class="line"> panic_on(sigprocmask(<span class="number">0</span>, &<span class="built_in">set</span>, <span class="literal">NULL</span>));</span><br><span class="line"> sigdelset(&<span class="built_in">set</span>, <span class="number">2</span>);</span><br><span class="line"> panic_on(sigaction(<span class="number">10</span>, &sig, <span class="literal">NULL</span>));</span><br><span class="line"> <span class="type">int</span> k = fork();</span><br><span class="line"> <span class="keyword">if</span> (k != <span class="number">0</span>) {</span><br><span class="line"> panic_on(sigprocmask(<span class="number">0</span>, <span class="literal">NULL</span>, &<span class="built_in">set</span>));</span><br><span class="line"> debugf(<span class="string">"Father: %d.\n"</span>, sigismember(&<span class="built_in">set</span>, <span class="number">2</span>));</span><br><span class="line"> panic_on(sigaction(<span class="number">10</span>, &sig, &swap_sig));</span><br><span class="line"> swap_sig.sa_handler(k); </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> debugf(<span class="string">"Child: %d.\n"</span>, sigismember(&<span class="built_in">set</span>, <span class="number">2</span>));<span class="comment">//检查子进程有没有错误继承父进程信号掩码</span></span><br><span class="line"> panic_on(sigaction(<span class="number">10</span>, &sig, &swap_sig));</span><br><span class="line"> swap_sig.sa_handler(k); <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></pre></td></tr></table></figure><ul><li>nullpointer_test</li></ul><figure class="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="meta">#<span class="keyword">include</span> <span class="string"><lib.h></span></span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">Segment fault appear!</span></span><br><span class="line"><span class="comment">test = 1.</span></span><br><span class="line"><span class="comment">test = 1.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">int</span> *test = <span class="literal">NULL</span>;</span><br><span class="line"><span class="type">int</span> t=<span class="number">1</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">sgv_handler</span><span class="params">(<span class="type">int</span> num)</span> {</span><br><span class="line"> debugf(<span class="string">"Segment fault appear!\n"</span>);</span><br><span class="line"> test=&t;</span><br><span class="line"> debugf(<span class="string">"test = %d.\n"</span>, *test);</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">int</span> argc, <span class="type">char</span> **argv)</span> {</span><br><span class="line"> <span class="type">sigset_t</span> <span class="built_in">set</span>;</span><br><span class="line"> sigemptyset(&<span class="built_in">set</span>);</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">sig</span>;</span></span><br><span class="line"> sig.sa_handler = sgv_handler;</span><br><span class="line"> sig.sa_mask = <span class="built_in">set</span>;</span><br><span class="line"> panic_on(sigaction(<span class="number">11</span>, &sig, <span class="literal">NULL</span>));</span><br><span class="line"> *test = <span class="number">10</span>;</span><br><span class="line"> debugf(<span class="string">"test = %d.\n"</span>, *test);</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><ul><li>mask_test</li></ul><figure class="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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><lib.h></span></span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">handler1 called handler2</span></span><br><span class="line"><span class="comment">handler2 called handler3</span></span><br><span class="line"><span class="comment">handler3 arrived!</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">handler1</span><span class="params">(<span class="type">int</span> num)</span>{</span><br><span class="line"> debugf(<span class="string">"handler1 called handler2\n"</span>);</span><br><span class="line"> kill(<span class="number">0</span>,<span class="number">2</span>);</span><br><span class="line">}</span><br><span class="line"><span class="type">void</span> <span class="title function_">handler2</span><span class="params">(<span class="type">int</span> num)</span>{</span><br><span class="line"> debugf(<span class="string">"handler2 called handler3\n"</span>);</span><br><span class="line"> kill(<span class="number">0</span>,<span class="number">3</span>);</span><br><span class="line">}</span><br><span class="line"><span class="type">void</span> <span class="title function_">handler3</span><span class="params">(<span class="type">int</span> num)</span>{</span><br><span class="line"> debugf(<span class="string">"handler3 arrived!\n"</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 class="class"><span class="keyword">struct</span> <span class="title">sigset_t</span> <span class="title">a</span>;</span></span><br><span class="line"> sigemptyset(&a);</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">act</span>;</span></span><br><span class="line"> act.sa_mask=a;</span><br><span class="line"> act.sa_handler=handler1;</span><br><span class="line"></span><br><span class="line"> sigaction(<span class="number">1</span>,&act,<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"> act.sa_handler=handler2;</span><br><span class="line"> a.sig[<span class="number">0</span>]|=(<span class="number">1</span><<<span class="number">2</span>); </span><br><span class="line"> act.sa_mask=a;</span><br><span class="line"></span><br><span class="line"> sigaction(<span class="number">2</span>,&act,<span class="literal">NULL</span>);</span><br><span class="line"> act.sa_mask.sig[<span class="number">0</span>]=<span class="number">0</span>;</span><br><span class="line"> act.sa_handler=handler3;</span><br><span class="line"></span><br><span class="line"> sigaction(<span class="number">3</span>,&act,<span class="literal">NULL</span>);</span><br><span class="line"> kill(<span class="number">0</span>,<span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>handler_test</li></ul><figure class="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="meta">#<span class="keyword">include</span> <span class="string"><lib.h></span></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">Reach handler, now the signum is 3!</span></span><br><span class="line"><span class="comment">Reach handler, now the signum is 2!</span></span><br><span class="line"><span class="comment">global = 2.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">int</span> global = <span class="number">0</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">handler</span><span class="params">(<span class="type">int</span> num)</span> {</span><br><span class="line"> debugf(<span class="string">"Reach handler, now the signum is %d!\n"</span>, num);</span><br><span class="line"> global++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TEST_NUM 2</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> **argv)</span> {</span><br><span class="line"> <span class="type">sigset_t</span> <span class="built_in">set</span>;</span><br><span class="line"> sigemptyset(&<span class="built_in">set</span>);</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">sig</span>;</span></span><br><span class="line"> sig.sa_handler = handler;</span><br><span class="line"> sig.sa_mask = <span class="built_in">set</span>;</span><br><span class="line"> panic_on(sigaction(TEST_NUM, &sig, <span class="literal">NULL</span>));</span><br><span class="line"> panic_on(sigaction(<span class="number">3</span>, &sig, <span class="literal">NULL</span>));</span><br><span class="line"> sigaddset(&<span class="built_in">set</span>, TEST_NUM);</span><br><span class="line"> panic_on(sigprocmask(<span class="number">0</span>, &<span class="built_in">set</span>, <span class="literal">NULL</span>));</span><br><span class="line"> kill(<span class="number">0</span>, TEST_NUM);</span><br><span class="line"> kill(<span class="number">0</span>, <span class="number">3</span>);</span><br><span class="line"> <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">10000000</span>; i++) {</span><br><span class="line"> ans += i;</span><br><span class="line"> }</span><br><span class="line"> panic_on(sigprocmask(<span class="number">1</span>, &<span class="built_in">set</span>, <span class="literal">NULL</span>));</span><br><span class="line"> debugf(<span class="string">"global = %d.\n"</span>, global);</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><h2 id="4、完成挑战性任务过程中遇到的问题及解决方案。"><a href="#4、完成挑战性任务过程中遇到的问题及解决方案。" class="headerlink" title="4、完成挑战性任务过程中遇到的问题及解决方案。"></a>4、完成挑战性任务过程中遇到的问题及解决方案。</h2><ul><li><p>内核态不能调用用户态函数!!!</p><p>解决方案:这是最令人困扰的问题。当时助教向我指出的时候,我就默默开始重写了。后来我的解决方法是在内核态保存好上下文,将函数指针作为参数传出去,并且回到用户态的地址设置为一个统一的env_signal_caller接口函数,用这个函数接受函数指针并且调用用户态函数。</p></li><li><p>信号重入机制,后来先服务,屏蔽的信号要留在原位等待频闭信号解除</p></li><li><p>解决方案:一开始钻了牛角尖,以为需要立马响应信号,果断排除了栈的数据结构,后来了解了信号是一种软中断,只需要在内核态返回用户态之前统一处理一次即可。于是在Env.h里,为env新加属性:</p><p>1、struct Sig_wait_list sig_wait_list;//等待信号列表</p><p>2、u_int top;//栈顶</p><p>3、u_int running_sig[MAX_SIGNALS];//等待处理的信号</p><p>这样在处理时候可以记录信号到来顺序并且后来先服务。</p></li></ul>]]></content>
</entry>
<entry>
<title>OS-Lab6</title>
<link href="/2023/06/30/OS-Lab6/"/>
<url>/2023/06/30/OS-Lab6/</url>
<content type="html"><![CDATA[<h1 id="Lab6实验报告"><a href="#Lab6实验报告" class="headerlink" title="Lab6实验报告"></a>Lab6实验报告</h1><h2 id="Thinkings"><a href="#Thinkings" class="headerlink" title="Thinkings"></a>Thinkings</h2><h3 id="Thinking6-1"><a href="#Thinking6-1" class="headerlink" title="Thinking6.1"></a>Thinking6.1</h3><figure class="highlight plaintext"><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><figure class="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="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"><span class="type">int</span> fildes[<span class="number">2</span>];</span><br><span class="line"><span class="type">char</span> buf[<span class="number">100</span>]; <span class="type">int</span> status;</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"> status = pipe(fildes);</span><br><span class="line"> <span class="keyword">if</span> (status == <span class="number">-1</span> ) { </span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"error\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">switch</span> (fork()) { </span><br><span class="line"> <span class="keyword">case</span> <span class="number">-1</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>: <span class="comment">/* 子进程 - 作为管道的写者 */</span> </span><br><span class="line"> close(fildes[<span class="number">0</span>]);</span><br><span class="line"> write(fildes[<span class="number">1</span>], <span class="string">"Hello world\n"</span>, <span class="number">20</span>);</span><br><span class="line"> close(fildes[<span class="number">1</span>]);</span><br><span class="line"> <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line"> <span class="keyword">default</span>: <span class="comment">/* 父进程 - 作为管道的读者 */</span> </span><br><span class="line"> close(fildes[<span class="number">1</span>]);</span><br><span class="line"> read(fildes[<span class="number">0</span>], buf, <span class="number">100</span>); </span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"child-process read:%s"</span>,buf); </span><br><span class="line"> close(fildes[<span class="number">0</span>]);</span><br><span class="line"> <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Thinking6-2"><a href="#Thinking6-2" class="headerlink" title="Thinking6.2"></a>Thinking6.2</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">上面这种不同步修改 pp_ref 而导致的进程竞争问题在 user/lib/fd.c 中的 dup 函数中也存在。请结合代码模仿上述情景,分析一下我们的 dup 函数中为什么会出现预想之外的情况? </span><br></pre></td></tr></table></figure><p><code>dup</code>的作用是把一个文件描述符的内容映射到另一个文件描述符,假如映射之前:</p><table><thead><tr><th align="center">p[0]</th><th align="center">p[1]</th><th align="center">pipe</th></tr></thead><tbody><tr><td align="center">x</td><td align="center">x</td><td align="center">x+1</td></tr></tbody></table><p>此时如果要映射读端,会先将<code>p[0]</code>的引用次数+1,再将<code>pipe</code>的引用次数+1,如果中间发生中断则pageref(p[0]) = pageref(pipe),别的进程会被认为是pipe关闭。</p><h3 id="Thinking6-3"><a href="#Thinking6-3" class="headerlink" title="Thinking6.3"></a>Thinking6.3</h3><figure class="highlight plaintext"><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><p>在mos操作系统中系统调用是原子操作,因为系统调用陷入内核态时候会屏蔽中断,系统调用结束后才会解除中断屏蔽。</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">// set the interruption bit</span></span><br><span class="line"><span class="variable">.macro</span> STI</span><br><span class="line"> mfc0 t0, CP0_STATUS</span><br><span class="line"> li t1, (STATUS_CU0 | <span class="number">0</span>x1)</span><br><span class="line"> <span class="keyword">or</span> t0, t1</span><br><span class="line"> mtc0 t0, CP0_STATUS</span><br><span class="line"> </span><br><span class="line"><span class="variable">.endm</span></span><br><span class="line"><span class="comment">// clear the interruption bit</span></span><br><span class="line"><span class="variable">.macro</span> CLI</span><br><span class="line"> mfc0 t0, CP0_STATUS</span><br><span class="line"> li t1, (STATUS_CU0 | <span class="number">0</span>x1)</span><br><span class="line"> <span class="keyword">or</span> t0, t1</span><br><span class="line"> <span class="keyword">xor</span> t0, <span class="number">0</span>x1</span><br><span class="line"> mtc0 t0, CP0_STATUS</span><br><span class="line"><span class="variable">.endm</span></span><br></pre></td></tr></table></figure><p>但不是所有的系统调用都是原子操作,比如linux操作系统中I/O系统调用就可以被比如时间片耗尽切换进程打断,但是也会保护现场,返回时可以继续进行余下操作。</p><h3 id="Thinking6-4"><a href="#Thinking6-4" class="headerlink" title="Thinking6.4"></a>Thinking6.4</h3><figure class="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">- 按照上述说法控制 pipe_close 中 fd 和 pipe unmap 的顺序,是否可以解决上述场景的进程竞争问题?给出你的分析过程。</span><br><span class="line">- 我们只分析了 close 时的情形,在 fd.c 中有一个 dup 函数,用于复制文件描述符。试想,如果要复制的文件描述符指向一个管道,那么是否会出现与 close 类似的问题?请模仿上述材料写写你的理解。</span><br></pre></td></tr></table></figure><p>可以解决这个问题,<code>pageref(fd) < pageref(pipe)</code>的时候<code>unmap</code>的时候先fd的ref减去1,此时依然有<code>pageref(fd) < pageref(pipe)</code>,从而避免了竞争。</p><p>dup要先将<code>pipe</code>的<code>ref</code>+1,再将<code>fd</code>的<code>ref</code>+1,从而始终保证<code>pageref(fd) < pageref(pipe)</code>。</p><h3 id="Thinking6-5"><a href="#Thinking6-5" class="headerlink" title="Thinking6.5"></a>Thinking6.5</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">elf_load_seg() 和 load_icode_mapper()函数是如何确保加载 ELF 文件时,bss 段数据被正确加载进虚拟内存空间。bss 段在 ELF 中并不占空间,但 ELF 加载进内存后,bss 段的数据占据了空间,并且初始值都是 0。请回顾 elf_load_seg() 和load_icode_mapper() 的实现,思考这一点是如何实现的?</span><br></pre></td></tr></table></figure><p>处理ELF加载的是lab3的<code>load_icode_mapper</code>,当<code>binsize < sgsize</code>的时候,缺少部分将会用0补齐,我记得课上也说了两个size之间是不一定相等的。</p><h3 id="Thinking6-6"><a href="#Thinking6-6" class="headerlink" title="Thinking6.6"></a>Thinking6.6</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">通过阅读代码空白段的注释我们知道,将标准输入或输出定向到文件,需要我们将其 dup 到 0 或 1 号文件描述符(fd)。那么问题来了:在哪步,0 和 1 被“安排”为标准输入和标准输出?请分析代码执行流程,给出答案。</span><br></pre></td></tr></table></figure><figure class="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="keyword">if</span> ((rightpipe = fork()) == <span class="number">0</span>) {</span><br><span class="line"> dup(p[<span class="number">0</span>],<span class="number">0</span>);</span><br><span class="line"> close(p[<span class="number">0</span>]);</span><br><span class="line"> close(p[<span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">goto</span> again;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"> dup(p[<span class="number">1</span>],<span class="number">1</span>);</span><br><span class="line"> close(p[<span class="number">1</span>]);</span><br><span class="line"> close(p[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">goto</span> runit;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Thinking6-7"><a href="#Thinking6-7" class="headerlink" title="Thinking6.7"></a>Thinking6.7</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">在 shell 中执行的命令分为内置命令和外部命令。在执行内置命令时 shell 不需要 fork 一个子 shell,如 Linux 系统中的 cd 命令。在执行外部命令时 shell 需要fork一个子 shell,然后子 shell 去执行这条命令。据此判断,在 MOS 中我们用到的 shell 命令是内置命令还是外部命令?请思考为什么Linux 的 cd 命令是内部命令而不是外部命令? </span><br></pre></td></tr></table></figure><p>在 MOS 中我们用到的 shell 命令是内置命令,因为它没有fork一个子 shell。Linux系统为了提高系统运行效率,将经常使用的轻量的命令在系统启动时一并加载这些命令到内存中供Shell随时调用,这部分命令即为内部命令,cd十分符合这个条件,因此是内置指令。</p><h3 id="Thinking6-8"><a href="#Thinking6-8" class="headerlink" title="Thinking6.8"></a>Thinking6.8</h3><figure class="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></pre></td><td class="code"><pre><span class="line">在你的 shell 中输入命令 ls.b | cat.b > motd。</span><br><span class="line">- 请问你可以在你的 shell 中观察到几次 spawn ?分别对应哪个进程?</span><br><span class="line">- 请问你可以在你的 shell 中观察到几次进程销毁?分别对应哪个进程?</span><br></pre></td></tr></table></figure><img src="/2023/06/30/OS-Lab6/1.png" class title="Problem"><p>2次spawn,销毁4个进程,可以对照着下面的图看。</p><img src="/2023/06/30/OS-Lab6/2.png" class title="Problem"><p>解析了两个符号,分别为“|”和“>”,因此fork了两个进程runcmd(),又分别spawn了一次,因此spawn为两次,同时spawn申请新的进程来运行指令,因此结束时候总共销毁了2*2个进程。</p><h2 id="实验难点"><a href="#实验难点" class="headerlink" title="实验难点"></a>实验难点</h2><p>最难的还是spawn函数,spawn 的流程可以分解如下:</p><p> • 从文件系统打开对应的文件(二进制 ELF,在我们的 OS 里是 *.b );</p><p> • 申请新的进程控制块;</p><p> • 将目标程序加载到子进程的地址空间中,并为它们分配物理页面;</p><p> • 为子进程初始化地址空间。对于栈空间,由于 spawn 需要将命令行参数传递给用户程序, 所以要将参数也写入用户栈中;</p><p> • 设置子进程的寄存器(栈指针 sp 和用户程序入口 EPC);</p><p> • 将父进程的共享页面映射到子进程的地址空间中;</p><p> • 这些都做完后,设置子进程可执行。</p><figure class="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">ELF_FOREACH_PHDR_OFF (ph_off, ehdr) {</span><br><span class="line"><span class="comment">// Read the program header in the file with offset 'ph_off' and length</span></span><br><span class="line"><span class="comment">// 'ehdr->e_phentsize' into 'elfbuf'.</span></span><br><span class="line"><span class="comment">// 'goto err1' on failure.</span></span><br><span class="line"><span class="keyword">if</span> ((r = seek(fd, ph_off)) < <span class="number">0</span>){</span><br><span class="line"><span class="keyword">goto</span> err1;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> ((r = readn(fd, elfbuf, ehdr->e_phentsize)) < <span class="number">0</span>){</span><br><span class="line"><span class="keyword">goto</span> err1;</span><br><span class="line">}<span class="keyword">else</span> <span class="keyword">if</span>(r!=ehdr->e_phentsize) {</span><br><span class="line">r = -E_NOT_EXEC;</span><br><span class="line"><span class="keyword">goto</span> err1;</span><br><span class="line">}</span><br><span class="line">Elf32_Phdr *ph = (Elf32_Phdr *)elfbuf;</span><br><span class="line"><span class="keyword">if</span> (ph->p_type == PT_LOAD) {</span><br><span class="line"><span class="type">void</span> *bin;</span><br><span class="line"><span class="comment">// Read and map the ELF data in the file at 'ph->p_offset' into our memory</span></span><br><span class="line"><span class="comment">// using 'read_map()'.</span></span><br><span class="line"><span class="comment">// 'goto err1' if that fails.</span></span><br><span class="line"><span class="keyword">if</span> ((r = read_map(fd,ph->p_offset,&bin)) < <span class="number">0</span>){</span><br><span class="line"><span class="keyword">goto</span> err1;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// Load the segment 'ph' into the child's memory using 'elf_load_seg()'.</span></span><br><span class="line"><span class="comment">// Use 'spawn_mapper' as the callback, and '&child' as its data.</span></span><br><span class="line"><span class="comment">// 'goto err1' if that fails.</span></span><br><span class="line"><span class="keyword">if</span> ((r = elf_load_seg(ph,bin,spawn_mapper,&child)) < <span class="number">0</span>){</span><br><span class="line"><span class="keyword">goto</span> err1;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我认为这是最困难的一段代码了,需要去类比lab1,回忆当时elf文件是怎么读入和装载的。</p><h2 id="体会与感想"><a href="#体会与感想" class="headerlink" title="体会与感想"></a>体会与感想</h2><p>Lab6在理论课交集很少,做的时候十分困难,阅读spawn代码的时候更是一脸迷茫,看了很久才知道大概的流程。包括elf的回顾,因为对之前的内容有所遗忘,所以需要了一定时间去复习之前的lab。对Shell的许多函数细节还没有完全理解对ELF文件结构和读取还需要复习。</p>]]></content>
</entry>
<entry>
<title>OS-Lab5</title>
<link href="/2023/06/30/OS-Lab5/"/>
<url>/2023/06/30/OS-Lab5/</url>
<content type="html"><![CDATA[<h1 id="Lab5实验报告"><a href="#Lab5实验报告" class="headerlink" title="Lab5实验报告"></a>Lab5实验报告</h1><h2 id="Thinkings"><a href="#Thinkings" class="headerlink" title="Thinkings"></a>Thinkings</h2><h3 id="Thinking5-1"><a href="#Thinking5-1" class="headerlink" title="Thinking5.1"></a>Thinking5.1</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">如果通过 kseg0 读写设备,那么对于设备的写入会缓存到 Cache 中。这是一种错误的行为,在实际编写代码的时候这么做会引发不可预知的问题。请思考:这么做这会引发什么问题?对于不同种类的设备(如我们提到的串口设备和 IDE 磁盘)的操作会有差异吗?可以从缓存的性质和缓存更新的策略来考虑。</span><br></pre></td></tr></table></figure><ul><li><p>数据一致性问题:由于写入的数据首先缓存到Cache中,而不是直接写入设备,可能会导致缓存和设备之间的数据不一致。其他使用内存访问的组件(如CPU)可能会读取到缓存中的旧数据,而不是设备上最新的数据。</p></li><li><p>时序问题:设备的读写操作通常需要特定的时序要求,直接将数据写入设备可以保证操作按照正确的时序进行。但是,通过将数据写入缓存,缓存的更新策略可能无法满足设备的时序要求,导致设备无法正常工作。</p></li></ul><p>缓存的性质:不同种类的设备可能具有不同的缓存策略和特性。一些设备可能具有可写回缓存,它们会在缓存满时才将数据写回设备,而一些设备可能具有直写缓存,每次写入缓存都会立即写入设备。</p><p>缓存更新策略:设备的缓存更新策略可能会影响到数据的一致性和时序。一些设备可能在每次写入缓存后立即更新设备,以确保数据一致性,而另一些设备可能会根据特定的缓存算法或策略来延迟或合并写入操作。</p><h3 id="Thinking5-2"><a href="#Thinking5-2" class="headerlink" title="Thinking5.2"></a>Thinking5.2</h3><figure class="highlight plaintext"><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><figure class="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="meta">#<span class="keyword">define</span> BY2FILE 256</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> FILE2BLK (BY2BLK / sizeof(struct File))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BY2BLK BY2PG</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BY2PG 4096</span></span><br></pre></td></tr></table></figure><p>4096/256=16块文件控制块,最多容纳1024*16=16384个文件,单个文件最大为1024*4KB=8MB。</p><h3 id="Thinking5-3"><a href="#Thinking5-3" class="headerlink" title="Thinking5.3"></a>Thinking5.3</h3><figure class="highlight plaintext"><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><p>0x40000000,1GB。</p><h3 id="Thinking5-4"><a href="#Thinking5-4" class="headerlink" title="Thinking5.4"></a>Thinking5.4</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">在本实验中,fs/serv.h、user/include/fs.h 等文件中出现了许多宏定义,试列举你认为较为重要的宏定义,同时进行解释,并描述其主要应用之处。</span><br></pre></td></tr></table></figure><figure class="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">define</span> BY2SECT 512 <span class="comment">//一个扇区大小(以字节为单位)</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SECT2BLK (BY2BLK / BY2SECT) <span class="comment">//每个磁盘块的扇区数</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DISKMAP 0x10000000 <span class="comment">//磁盘块缓存基址</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DISKMAX 0x40000000 <span class="comment">//磁盘块缓存大小</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BY2BLK BY2PG <span class="comment">//每个磁盘块大小(字节作为单位)</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BIT2BLK (BY2BLK * 8) <span class="comment">//每个磁盘块大小(bit作为单位)</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXNAMELEN 128 <span class="comment">//最大文件名长度</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXPATHLEN 1024 <span class="comment">//最大路径字符串长度</span></span></span><br></pre></td></tr></table></figure><h3 id="Thinking5-5"><a href="#Thinking5-5" class="headerlink" title="Thinking5.5"></a>Thinking5.5</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">在 Lab4“系统调用与 fork”的实验中我们实现了极为重要的 fork 函数。那么 fork 前后的父子进程是否会共享文件描述符和定位指针呢?请在完成上述练习的基础上编写一个程序进行验证。 </span><br></pre></td></tr></table></figure><figure class="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 class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line"><span class="type">int</span> r, num, n;</span><br><span class="line"><span class="type">char</span> buf[<span class="number">200</span>];</span><br><span class="line">num = open(<span class="string">"/newmotd"</span>, O_RDWR);</span><br><span class="line"><span class="keyword">if</span> ((r = fork()) == <span class="number">0</span>) {</span><br><span class="line">n = read(num,buf, <span class="number">5</span>);</span><br><span class="line">debugf(<span class="string">"[child] buffer is \'%s\'\n"</span>, buf);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">n = read(num,buf, <span class="number">5</span>);</span><br><span class="line">debugf(<span class="string">"[father] buffer is \'%s\'\n"</span>, buf);</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="comment">//若父子进程不共享文件描述符和定位指针则两次输出相同,反之则共享。</span></span><br></pre></td></tr></table></figure><p>结果:</p><img src="/2023/06/30/OS-Lab5/1.png" class title="This is an example image"><h3 id="Thinking5-6"><a href="#Thinking5-6" class="headerlink" title="Thinking5.6"></a>Thinking5.6</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">请解释 File, Fd, Filefd 结构体及其各个域的作用。比如各个结构体会在哪些过程中被使用,是否对应磁盘上的物理实体还是单纯的内存数据等。</span><br></pre></td></tr></table></figure><p>Fd用于记录文件的基本信息,File Describtor:</p><figure class="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">// file descriptor</span><br><span class="line">struct Fd {</span><br><span class="line"> u_int fd_dev_id; // 外设的id</span><br><span class="line"> u_int fd_offset; // 读写的偏移量</span><br><span class="line"> u_int fd_omode; // 打开方式,包括只读、只写、读写</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>Filefd用于记录文件的详细信息,包括文件id,文件描述符和文件本身:</p><figure class="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="class"><span class="keyword">struct</span> <span class="title">Filefd</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Fd</span> <span class="title">f_fd</span>;</span> <span class="comment">// file descriptor</span></span><br><span class="line"> u_int f_fileid; <span class="comment">// 文件的id</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">File</span> <span class="title">f_file</span>;</span> <span class="comment">// 真正的文件本身</span></span><br><span class="line"> };</span><br></pre></td></tr></table></figure><p>Open是打开文件:</p><figure class="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="class"><span class="keyword">struct</span> <span class="title">Open</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">File</span> *<span class="title">o_file</span>;</span> <span class="comment">// 指向打开的文件</span></span><br><span class="line"> u_int o_fileid; <span class="comment">// 打开文件的id</span></span><br><span class="line"> <span class="type">int</span> o_mode; <span class="comment">// 打开方式</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Filefd</span> *<span class="title">o_ff</span>;</span> <span class="comment">// 指向读写的位置(偏移)</span></span><br><span class="line"> };</span><br></pre></td></tr></table></figure><h3 id="Thinking5-7"><a href="#Thinking5-7" class="headerlink" title="Thinking5.7"></a>Thinking5.7</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">图5.7中有多种不同形式的箭头,请解释这些不同箭头的差别,并思考我们的操作系统是如何实现对应类型的进程间通信的。 </span><br></pre></td></tr></table></figure><img src="/2023/06/30/OS-Lab5/2.png" class title="This is an example image"><ul><li>虚线:生命线</li><li>实心箭头:传递信息,ENV_CREATE,ipc_send</li><li>虚线箭头:传递异步消息,ipc_send</li></ul><p>操作系统是通过IPC进行进程间通信的。</p><h2 id="实验难点"><a href="#实验难点" class="headerlink" title="实验难点"></a>实验难点</h2><ul><li>read(int fdnum, void *buf, u_int n):<ul><li>调用fd_lookup查找fdnum对应的fd</li><li>调用dev_lookup查找dev_id对应的dev</li><li>调用dev_read从offset处读取n个字节到buf,更新offset</li></ul></li><li>open(const char *path, int mode):<ul><li>调用fd_alloc分配出一个空闲的fd,用于记录即将打开的文件</li><li>调用fsipc_open打开path路径上的文件(由serve进程将file信息加载到fd上)</li><li>调用fd2data获取fd对应的用于存储数据的首地址</li><li>for循环遍历,调用syscall_mem_alloc以va起始开辟存储空间,调用fsipc_map</li><li>返回文件对应的fdnum</li></ul></li><li>fsipc(u_int type, void *fsreq, u_int dstva, u_int *perm):<ul><li>将请求发送给serve进程</li><li>设置用户进程接收信息的地址,允许接收,此时接收serve处理后传回的信息</li></ul></li><li>fsipc_open(const char *path, u_int omode, struct Fd *fd):将打开文件的路径、打开方式传递给serve</li><li>serve_open(u_int envid, struct Fsreq_open *rq):提供文件打开服务<ul><li>从rq中回去请求信息</li><li>调用open_alloc分配打开文件</li><li>调用file_open按路径打开文件</li><li>将文件信息传递给打开文件</li><li>将打开文件通过进程通信传递给用户进程</li></ul></li><li>ipc_send(..,..,..,..)…….</li></ul><p>之所以觉得这些函数困难是因为在课上写openat函数的时候,对这些函数十分陌生,说明课下没有整体理解流程,课上看到题的时候还是很懵的,但万幸在课上理解了大概原理,过了exam。</p><h2 id="感想体会"><a href="#感想体会" class="headerlink" title="感想体会"></a>感想体会</h2><p>lab5<del>十分难,十分难,十分难</del>,需要认真阅读,理解调用逻辑。在做课下的时候,理解文件控制块,磁盘块,文件描述符等等结构体,理解磁盘块的结构,理解磁盘的组成,知道了一块磁盘由许多块磁片组成,因此有许许多多磁头。lab5-1课上完成的不错,extra也得了80分,最后的20分强测也是本地测试正确但是没有时间提交了。lab5-2课上的时候,课下实验做得一知半解,全程在仿写课程组提供的带open的函数,对每一步都模棱两可的,不知道自己模仿的逻辑对不对,属于是靠运气过了。现在想来虽然课下有尝试讲出用户进程请求文件系统服务的逻辑链,比如一个函数抛出错误码以后会,ipc_send,通知其他进程,请求相应的服务。整理和撰写这份实验报告的时候又学到了许多,比如为验证父子进程文件描述符和定位指针的共享编写测试代码。OS上机的漫漫长路过去了,十分有收获,接下来把lab6和挑战性任务完成!</p>]]></content>
</entry>
<entry>
<title>OS-Lab4</title>
<link href="/2023/06/30/OS-Lab4/"/>
<url>/2023/06/30/OS-Lab4/</url>
<content type="html"><![CDATA[<h1 id="Lab4"><a href="#Lab4" class="headerlink" title="Lab4"></a>Lab4</h1><h2 id="Thinkings"><a href="#Thinkings" class="headerlink" title="Thinkings"></a>Thinkings</h2><h3 id="Thinking4-1"><a href="#Thinking4-1" class="headerlink" title="Thinking4.1"></a>Thinking4.1</h3><figure class="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></pre></td><td class="code"><pre><span class="line">• 内核在保存现场的时候是如何避免破坏通用寄存器的?</span><br><span class="line">• 系统陷入内核调用后可以直接从当时的 $a0-$a3 参数寄存器中得到用户调用 msyscall留下的信息吗?</span><br><span class="line">• 我们是怎么做到让 sys 开头的函数“认为”我们提供了和用户调用 msyscall 时同样的参数的?</span><br><span class="line">• 内核处理系统调用的过程对 Trapframe 做了哪些更改?这种修改对应的用户态的变化是什么?</span><br></pre></td></tr></table></figure><ul><li>调用了SAVE_ALL来保存通用寄存器。</li><li>可以,因为陷入内核时这四个寄存器的值未被破坏。</li><li>将调用函数时都将前四个参数按顺序放入$a0-$a3寄存器,后两个参数按顺序压栈,维护栈指针即可。</li><li>改变了Trapframe中寄存器v0的值,用于保存在用户态中调用系统调用函数的返回值;改变了EPC的值,使得程序返回用户态后能够从正确的位置继续执行。</li></ul><h3 id="Thinking4-2"><a href="#Thinking4-2" class="headerlink" title="Thinking4.2"></a>Thinking4.2</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">为什么 envid2env 中需要判断 e->env_id != envid的情况?如果没有这步判断会发生什么情况?</span><br></pre></td></tr></table></figure><figure class="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="keyword">if</span>(envid==<span class="number">0</span>){</span><br><span class="line"> *penv=curenv;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"> e=&envs[ENVX(envid)];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (e->env_status == ENV_FREE || e->env_id != envid) {</span><br><span class="line"><span class="keyword">return</span> -E_BAD_ENV;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面这是envid2env的部分函数,可以看出envs数组的索引只取envid的后十位,但可能前22位与envs数组取出的envid不同,因此需要再次判断e->env_id != envid。</p><h3 id="Thinking4-3"><a href="#Thinking4-3" class="headerlink" title="Thinking4.3"></a>Thinking4.3</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">请回顾 kern/env.c 文件中 mkenvid() 函数的实现,该函数不会返回 0,请结合系统调用和 IPC 部分的实现与envid2env() 函数的行为进行解释。 </span><br></pre></td></tr></table></figure><figure class="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"> u_int <span class="title function_">mkenvid</span><span class="params">(<span class="keyword">struct</span> Env *e)</span> {</span><br><span class="line"><span class="type">static</span> u_int i = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">return</span> ((++i) << (<span class="number">1</span> + LOG2NENV)) | (e - envs);</span><br><span class="line">}</span><br><span class="line"><span class="type">int</span> <span class="title function_">envid2env</span><span class="params">(u_int envid, <span class="keyword">struct</span> Env **penv, <span class="type">int</span> checkperm)</span> {</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Env</span> *<span class="title">e</span>;</span></span><br><span class="line"><span class="keyword">if</span>(envid==<span class="number">0</span>){</span><br><span class="line">*penv=curenv;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line">e=&envs[ENVX(envid)];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (e->env_status == ENV_FREE || e->env_id != envid) {</span><br><span class="line"><span class="keyword">return</span> -E_BAD_ENV;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(checkperm&&!(e==curenv||e->env_parent_id==curenv->env_id)){</span><br><span class="line"><span class="keyword">return</span> -E_BAD_ENV;</span><br><span class="line">}</span><br><span class="line">*penv = e;</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><p>mkenvid()用于申请一个新的env_id用于分配新的进程块,从逻辑上来看,0代表当前进程,因此在申请新的env_id不会返回0;从函数角度来看,((++i) << (1 + LOG2NENV)) | (e - envs)想要为0,首先满足这是分配的第一个内存,因此此时((++i) << (1 + LOG2NENV))不为0,所以它或上(e - envs)不为0,所以mkenvid()不会返回0。</p><h3 id="Thinking4-4"><a href="#Thinking4-4" class="headerlink" title="Thinking4.4"></a>Thinking4.4</h3><figure class="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></pre></td><td class="code"><pre><span class="line">关于 fork 函数的两个返回值,下面说法正确的是:</span><br><span class="line">A、fork 在父进程中被调用两次,产生两个返回值</span><br><span class="line">B、fork 在两个进程中分别被调用一次,产生两个不同的返回值</span><br><span class="line">C、fork 只在父进程中被调用了一次,在两个进程中各产生一个返回值</span><br><span class="line">D、fork 只在子进程中被调用了一次,在两个进程中各产生一个返回值</span><br></pre></td></tr></table></figure><p>选C。父进程通过调用fork函数生成新的子进程,因此fork只被调用了一次,同时子进程是在fork函数过程中通过syscall_exofork()函数产生的进程,且进程现场与父进程一致,包括pc的值,因此父子进程接下去会并发执行相同的程序,因此它们在fork()结束时会返回各自的值,因此选C。</p><h3 id="Thinking4-5"><a href="#Thinking4-5" class="headerlink" title="Thinking4.5"></a>Thinking4.5</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">我们并不应该对所有的用户空间页都使用 duppage 进行映射。那么究竟哪些用户空间页应该映射,哪些不应该呢?请结合 kern/env.c 中 env_init 函数进行的页面映射、include/mmu.h 里的内存布局图以及本章的后续描述进行思考。</span><br></pre></td></tr></table></figure><p>对页表存在映射(即相应一、二级页表项的PTE_D位为1)的页进行映射。</p><figure class="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">map_segment(base_pgdir, <span class="number">0</span>, PADDR(pages), UPAGES, ROUND(npage * <span class="keyword">sizeof</span>(<span class="keyword">struct</span> Page), BY2PG), PTE_G);</span><br><span class="line">map_segment(base_pgdir, <span class="number">0</span>, PADDR(envs), UENVS, ROUND(NENV * <span class="keyword">sizeof</span>(<span class="keyword">struct</span> Env), BY2PG),PTE_G);</span><br></pre></td></tr></table></figure><img src="/2023/06/30/OS-Lab4/1.png" class title="Problem"><p>这两个函数将页表和进程控制块虚拟地址与物理地址进行映射。</p><h3 id="Thinking4-6"><a href="#Thinking4-6" class="headerlink" title="Thinking4.6"></a>Thinking4.6</h3><figure class="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></pre></td><td class="code"><pre><span class="line">在遍历地址空间存取页表项时你需要使用到 vpd 和 vpt 这两个指针,请参考 user/include/lib.h 中的相关定义,思考并回答这几个问题:</span><br><span class="line">• vpt 和 vpd 的作用是什么?怎样使用它们?</span><br><span class="line">• 从实现的角度谈一下为什么进程能够通过这种方式来存取自身的页表?</span><br><span class="line">• 它们是如何体现自映射设计的?</span><br><span class="line">• 进程能够通过这种方式来修改自己的页表项吗?</span><br></pre></td></tr></table></figure><ul><li>vpt,virtual page table二级页表; vpd,virtual page directory 一级页表(页目录)。将它们作为数组名使用。如vpt[xxx],vbd[xxx]。</li></ul><figure class="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> vpt ((volatile Pte *)UVPT)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> vpd ((volatile Pde *)(UVPT + (PDX(UVPT) << PGSHIFT)))</span></span><br></pre></td></tr></table></figure><img src="/2023/06/30/OS-Lab4/2.png" class title="Problem"><ul><li>vpt和vpd是数组名,同时也是首指针,指向mmu.h中的Uesr VPT这一块中的起始地址和页目录自映射地址。因此vpt就是二级页表起始地址,而vpd就是页目录起始地址。</li><li>从vpd的定义为<code>((volatile Pde *)(UVPT + (PDX(UVPT) << PGSHIFT)))</code>就能看出其为页目录自映射的。</li><li>不可以,页表项只能由操作系统修改。</li></ul><h3 id="Thinking4-7"><a href="#Thinking4-7" class="headerlink" title="Thinking4.7"></a>Thinking4.7</h3><figure class="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></pre></td><td class="code"><pre><span class="line">在 do_tlb_mod 函数中,你可能注意到了一个向异常处理栈复制 Trapframe运行现场的过程,请思考并回答这几个问题:</span><br><span class="line">• 这里实现了一个支持类似于“异常重入”的机制,而在什么时候会出现这种“异常重入”?</span><br><span class="line">• 内核为什么需要将异常的现场 Trapframe 复制到用户空间?</span><br></pre></td></tr></table></figure><ul><li>中断处理过程中发生各种异常(如缺页异常)导致中断,此时需要保留现场处理新遇到的异常。</li><li>因为异常的处理需要用户自定义,所以在用户态进行,因此需要把TrapFrame复制给用户空间。</li></ul><h3 id="Thinking4-8"><a href="#Thinking4-8" class="headerlink" title="Thinking4.8"></a>Thinking4.8</h3><figure class="highlight plaintext"><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><ul><li>方便用户自定义异常处理方法,更加灵活。</li><li>在用户态操作,不会访问内核空间,更加安全。</li></ul><h3 id="Thinking4-9"><a href="#Thinking4-9" class="headerlink" title="Thinking4.9"></a>Thinking4.9</h3><figure class="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></pre></td><td class="code"><pre><span class="line">请思考并回答以下几个问题:</span><br><span class="line">• 为什么需要将 syscall_set_tlb_mod_entry 的调用放置在 syscall_exofork 前?</span><br><span class="line">• 如果放置在写时复制保护机制完成之后会有怎样的效果?</span><br></pre></td></tr></table></figure><ul><li>因为 syscall_exofork 会复制当前进程的Trapframe给子进程,在此之前需要把当前进程的tlb_mod_entry设置为cow_entry,这样设置一次即可,子进程会进行复制,不然调换顺序还需复制一次,十分麻烦。</li><li>这更不合理,没有给子进程的每一页设置cow_entry作为处理入口,根本无法完成写时复制保护机制。</li></ul><h2 id="难点分析"><a href="#难点分析" class="headerlink" title="难点分析"></a>难点分析</h2><ul><li><p>首先明白为什么需要系统调用,是为了保护我们的操作系统,只有在内核态才能修改比较“核心”的数据,比如页表项,输入输出操作等。当用户态需要修改这些核心数据,必须陷入内核态,由中断服务函数进行完成。内核态中处理完毕,返回用户态,并将返回值(位于$v0寄存器)传递回去,一层层回到调用处。</p></li><li><p>中断号,中断服务函数,用户态与内核态之间的相近“函数”我认为是本次实验的难点。sys_开头的函数,SYS_开头的函数,msyscall,syscall_开头的函数等等,十分容易混淆。</p></li><li><p>IPC 是微内核最重要的机制之一,目的是使得两个进程之间可以通讯,需要通过系统调用来实现。通讯最直观的一种理解就是交换数据。两个进程之间之所以没法相互交换数据,是因为<strong>各个进程的地址空间相互独立</strong>。沟通两个进程,自然需要一个<strong>权限凌驾两个进程之上的存在</strong>来进行操作,即内核态。ipc通信机制利用的正是系统调用,并且ipc_recv通过轮询是否收到信息来阻塞未收到信息的进程,通过schedule来放弃当前进程实现。</p><pre><code><figure class="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">u_int env_ipc_value; <span class="comment">// 传递的数据值</span></span><br><span class="line">u_int env_ipc_from; <span class="comment">// 发送者的进程id</span></span><br><span class="line">u_int env_ipc_recving; <span class="comment">// 进程是否阻塞,从而能够接收。0为不能接收,1为可以接收。</span></span><br><span class="line">u_int env_ipc_dstva; <span class="comment">// 接收物理页面的虚拟地址</span></span><br><span class="line">u_int env_ipc_perm; <span class="comment">// 接收页面的保护位</span></span><br></pre></td></tr></table></figure></code></pre></li></ul><h2 id="实验体会"><a href="#实验体会" class="headerlink" title="实验体会"></a>实验体会</h2><p>这次实验难度我觉得十分大,尤其是ipc通信那一块,课上的题目我没有做出来,ipc本质上是系统调用的一种情况而已,但我总是会将其想复杂,应该是我对于系统调用的流程还不是十分熟悉把。不过当我写这份实验报告的时候,我已经弄明白用户态调用函数陷入内核态,再由内核态的终端服务函数来完成服务,最后返回内核态继续执行的流程了,也感觉十分富有逻辑性,设计十分巧妙。lab4让我学到了很多有关用户进程的知识,也让我实操了系统调用,加深了对用户态和内核态的理解,感觉收获很大。</p>]]></content>
</entry>
<entry>
<title>OS-Lab3</title>
<link href="/2023/06/30/OS-Lab3/"/>
<url>/2023/06/30/OS-Lab3/</url>
<content type="html"><![CDATA[<h1 id="Lab3实验报告"><a href="#Lab3实验报告" class="headerlink" title="Lab3实验报告"></a>Lab3实验报告</h1><h2 id="Thinkings"><a href="#Thinkings" class="headerlink" title="Thinkings"></a>Thinkings</h2><h3 id="Thinking3-1"><a href="#Thinking3-1" class="headerlink" title="Thinking3.1"></a>Thinking3.1</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">请结合MOS中的页目录自映射应用解释代码中 e->env_pgdir[PDX(UVPT)]= PADDR(e->env_pgdir) | PTE_V 的含义。</span><br></pre></td></tr></table></figure><p>PADDR(e->env_pgdir)是e的页目录的物理地址,PDX(UVPT)指的是索引这一个进程的页目录.的一级页号,将这个一级页号加上e的页目录地址,希望得到的页表项是能索引到e的页目录。将这个页表项写为PADDR(e->env_pgdir) | PTE_V,就能建立页目录自映射。</p><h3 id="Thinking3-2"><a href="#Thinking3-2" class="headerlink" title="Thinking3.2"></a>Thinking3.2</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">elf_load_seg 以函数指针的形式,接受外部自定义的回调函数 map_page。请你找到与之相关的 data 这一参数在此处的来源,并思考它的作用。没有这个参数可不可以?为什么?</span><br></pre></td></tr></table></figure><figure class="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="type">size_t</span> ph_off = (ehdr)->e_phoff;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> _ph_idx = <span class="number">0</span>; _ph_idx < (ehdr)->e_phnum; ++_ph_idx, (ph_off) += (ehdr)->e_phentsize) {</span><br><span class="line"> Elf32_Phdr *ph = (Elf32_Phdr *)(binary + ph_off);</span><br><span class="line"> <span class="keyword">if</span> (ph->p_type == PT_LOAD) {</span><br><span class="line"> panic_on(elf_load_seg(ph, binary + ph->p_offset,load_icode_mapper, e));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>map_page在这里是load_icode_mapper,data来源是(ehdr)->e_phoff,每次循环加ehdr-> e_phentsize。不可以没有data,因为elf_load_seg 函数会从 ph 中获取 va(该段需要被加载到的虚地址)、sgsize(该段在内存中的大小)、bin_size(该段在文件中的大小)和 perm(该段被加载时的页面权限)。</p><h3 id="Thinking3-3"><a href="#Thinking3-3" class="headerlink" title="Thinking3.3"></a>Thinking3.3</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">结合 elf_load_seg 的参数和实现,考虑该函数需要处理哪些页面加载的情况。</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">elf_load_seg</span><span class="params">(Elf32_Phdr *ph, <span class="type">const</span> <span class="type">void</span> *bin, <span class="type">elf_mapper_t</span> map_page, <span class="type">void</span> *data)</span></span><br></pre></td></tr></table></figure><ul><li>加载该段的所有数据(bin)中的所有内容到内存(va)。 </li><li>如果该段在文件中的内容的大小达不到为填入这段内容新分配的页面大小,即分配了新的页面但没能填满(如 .bss 区域),那么余下的部分用 0 来填充。</li></ul><h3 id="Thinking3-4"><a href="#Thinking3-4" class="headerlink" title="Thinking3.4"></a>Thinking3.4</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">你认为这里的 env_tf.cp0_epc 存储的是物理地址还是虚拟地址?</span><br></pre></td></tr></table></figure><p>虚拟地址,因为ehdr->entry储存的是elf文件入口虚拟地址。</p><h3 id="Thinking3-5"><a href="#Thinking3-5" class="headerlink" title="Thinking3.5"></a>Thinking3.5</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">试找出 0、1、2、3 号异常处理函数的具体实现位置。</span><br></pre></td></tr></table></figure><p>在genex.S中</p><figure class="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">.macro BUILD_HANDLER exception handler</span><br><span class="line"><span class="title function_">NESTED</span><span class="params">(handle_\exception, TF_SIZE, zero)</span></span><br><span class="line">move a0, sp</span><br><span class="line">jal \handler</span><br><span class="line">j ret_from_exception</span><br><span class="line"><span class="title function_">END</span><span class="params">(handle_\exception)</span></span><br><span class="line">.endm</span><br><span class="line"><span class="comment">//0号异常处理</span></span><br><span class="line"><span class="title function_">NESTED</span><span class="params">(handle_int, TF_SIZE, zero)</span></span><br><span class="line">mfc0 t0, CP0_CAUSE</span><br><span class="line">mfc0 t2, CP0_STATUS</span><br><span class="line">and t0, t2</span><br><span class="line">andi t1, t0, STATUS_IM4</span><br><span class="line">bnez t1, timer_irq</span><br><span class="line"><span class="comment">// <span class="doctag">TODO:</span> handle other irqs</span></span><br><span class="line">timer_irq:</span><br><span class="line">sw zero, <span class="params">(KSEG1 | DEV_RTC_ADDRESS | DEV_RTC_INTERRUPT_ACK)</span></span><br><span class="line">li a0, 0</span><br><span class="line">j schedule</span><br><span class="line"><span class="title function_">END</span><span class="params">(handle_int)</span></span><br><span class="line">BUILD_HANDLER tlb do_tlb_refill<span class="comment">//2,3号异常处理</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !defined(LAB) || LAB >= 4</span></span><br><span class="line">BUILD_HANDLER mod do_tlb_mod<span class="comment">//1号异常处理</span></span><br><span class="line">BUILD_HANDLER sys do_syscall<span class="comment">//8号异常处理</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">BUILD_HANDLER reserved do_reserved<span class="comment">//其余异常处理</span></span><br></pre></td></tr></table></figure><h3 id="Thinking3-6"><a href="#Thinking3-6" class="headerlink" title="Thinking3.6"></a>Thinking3.6</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">阅读 init.c、kclock.S、env_asm.S 和 genex.S 这几个文件,并尝试说出enable_irq 和 timer_irq 中每行汇编代码的作用。 </span><br></pre></td></tr></table></figure><ul><li><p>enable_irq</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">LEAF(enable<span class="emphasis">_irq)</span></span><br><span class="line"><span class="emphasis">li t0, (STATUS_</span>CU0 | STATUS<span class="emphasis">_IM4 | STATUS_</span>IEc)</span><br><span class="line"><span class="code">//向t0存入允许的中断</span></span><br><span class="line"><span class="code">mtc0 t0, CP0_STATUS</span></span><br><span class="line"><span class="code">//向CP0_STATUS寄存器写入允许的中断设置</span></span><br><span class="line"><span class="code">jr ra</span></span><br><span class="line"><span class="code">//返回</span></span><br><span class="line"><span class="code">END(enable_irq)</span></span><br></pre></td></tr></table></figure></li><li><p>timer_irq</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">timer<span class="emphasis">_irq:</span></span><br><span class="line"><span class="emphasis">sw zero, (KSEG1 | DEV_</span>RTC<span class="emphasis">_ADDRESS | DEV_</span>RTC<span class="emphasis">_INTERRUPT_</span>ACK)</span><br><span class="line"><span class="code">//kseg1处向时钟外设的中断位置写入0,表示响应中断</span></span><br><span class="line"><span class="code">li a0, 0</span></span><br><span class="line"><span class="code">//将0作为函数参数</span></span><br><span class="line"><span class="code">j schedule</span></span><br><span class="line"><span class="code">//跳转到进程切换</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="Thinking3-7"><a href="#Thinking3-7" class="headerlink" title="Thinking3.7"></a>Thinking3.7</h3><figure class="highlight plaintext"><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><p>thinking3.6的timer_irq在响应时间中断后会将参数为0(yield==0)并跳转到schedule函数。</p><figure class="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="type">void</span> <span class="title function_">schedule</span><span class="params">(<span class="type">int</span> yield)</span> {</span><br><span class="line"><span class="type">static</span> <span class="type">int</span> count = <span class="number">0</span>; <span class="comment">// remaining time slices of current env</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Env</span> *<span class="title">e</span> =</span> curenv;</span><br><span class="line"><span class="keyword">if</span>(yield==<span class="number">1</span>||count==<span class="number">0</span>||e==<span class="literal">NULL</span>||e->env_status!=ENV_RUNNABLE){</span><br><span class="line"><span class="keyword">if</span>(curenv!=<span class="literal">NULL</span>&&e->env_status==ENV_RUNNABLE){</span><br><span class="line">TAILQ_INSERT_TAIL(&env_sched_list,e,env_sched_link);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(TAILQ_EMPTY(&env_sched_list)){</span><br><span class="line">panic(<span class="string">"error"</span>);</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line">e=TAILQ_FIRST(&env_sched_list);</span><br><span class="line">TAILQ_REMOVE(&env_sched_list,e,env_sched_link);</span><br><span class="line">count=e->env_pri;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">count--;</span><br><span class="line">env_run(e);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在schedule函数中因为yield不为1,且其余条件都满足进程切换,会将当前进程插入到进程链表的尾部,然后取出进程链表env_sched_list第一个进程作为当前进程,并且env_run。这就做到了每个时间片结束对进程进行切换。</p><h2 id="实验难点"><a href="#实验难点" class="headerlink" title="实验难点"></a>实验难点</h2><ul><li>前半部分的进程切换虽然复杂但是理解起来不困难,十分自然,但是加载二进制镜像理解起来十分不容易。load_icode中调用了elf_load_seg函数,而 elf_load_seg调用了load_icode_mapper,load_icode_mapper最终调用了memcpy完成内存复制。</li><li>每个部分理解起来并不困难,当将串联就不是很清晰了。</li></ul><img src="/2023/06/30/OS-Lab3/1.png" class title="This is an example image"><h2 id="实验体会"><a href="#实验体会" class="headerlink" title="实验体会"></a>实验体会</h2><p>有了lab2的数据结构记忆唤醒,理解lab3的空闲env和调度中的env队列就变得相当容易了。这个lab与上学期计组的p7结合得十分紧密,当时那几个协处理寄存器又一次理解了它们的作用,有记录中断允许位的,有记录异常号的,方便直接获取已经储存在相应异常号的下标的异常处理函数入口地址的数组。模拟中断的方式也是相同,往指定的位置写入值,相应异常便是在那个指定的位置恢复特定的值。同时这次试验需要自己的debug来定位出错的点,可以选择使用<code>gexuml</code>的调试方法,但是我觉得还不够直观和精确,于是选择了在可能出错的语句前后printk值来检查当前程序在哪开始出问题了,精准定位。</p>]]></content>
</entry>
<entry>
<title>BUAA-OO-第四单元UML图书管理系统</title>
<link href="/2023/06/30/BUAA-OO-%E7%AC%AC%E5%9B%9B%E5%8D%95%E5%85%83UML%E5%9B%BE%E4%B9%A6%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/"/>
<url>/2023/06/30/BUAA-OO-%E7%AC%AC%E5%9B%9B%E5%8D%95%E5%85%83UML%E5%9B%BE%E4%B9%A6%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/</url>
<content type="html"><![CDATA[<h1 id="BUAA-OO-UML图书管理系统"><a href="#BUAA-OO-UML图书管理系统" class="headerlink" title="BUAA-OO-UML图书管理系统"></a>BUAA-OO-UML图书管理系统</h1><h2 id="本单元所实践的正向建模与开发"><a href="#本单元所实践的正向建模与开发" class="headerlink" title="本单元所实践的正向建模与开发"></a>本单元所实践的正向建模与开发</h2><p>正向建模与开发是一种软件工程方法,通过逐步明确需求和功能、按照系统设计的结果进行编码和实现、验证和测试系统,并最终部署和维护系统的过程。它强调系统开发的连贯性、高质量和与用户的密切合作。</p><p>这次作业我通过了先设计了UML类图,厘清每个类之间的关系后,在纸上画出大概类图之后,再进行编码。</p><h2 id="本单元作业的架构设计"><a href="#本单元作业的架构设计" class="headerlink" title="本单元作业的架构设计"></a>本单元作业的架构设计</h2><img src="/2023/06/30/BUAA-OO-%E7%AC%AC%E5%9B%9B%E5%8D%95%E5%85%83UML%E5%9B%BE%E4%B9%A6%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/1.jpg" class title="This is an example image"><p>就像上面类图所示,Request是联系各个类之间的纽带,是一种行为模式。purchasingDepartment处理Reader发出的Request,再把这些Request分发处理。设计架构时做到“高内聚、低耦合”,将功能的具体操作都放到每个Library的管理员中,通过这样的方式来做到架构整体的逻辑更加清晰。</p><h2 id="分析代码设计和UML模型设计之间的追踪关系"><a href="#分析代码设计和UML模型设计之间的追踪关系" class="headerlink" title="分析代码设计和UML模型设计之间的追踪关系"></a>分析代码设计和UML模型设计之间的追踪关系</h2><p>UML模型是一种图形化的建模语言,用于描述系统的结构和行为。在软件开发过程中,通过使用UML模型,开发人员可以在更高的层次上思考和设计系统的组件、类、接口等。这些UML模型元素提供了对代码设计的抽象和概念,有助于更清晰地理解和定义代码的结构和关系。在我的架构设计中,大体与一开始的设计相符,但是中间的某些细节处理需要新增方法来解决。</p><h2 id="四个单元中架构设计思维的演进"><a href="#四个单元中架构设计思维的演进" class="headerlink" title="四个单元中架构设计思维的演进"></a>四个单元中架构设计思维的演进</h2><p>第一单元比较困难,虽然之前学过pre课程,但是相隔时间也有点久远有些遗忘。从面向过程到面向对象的转变对当时的我十分困难,花了两天多的时间建模,一步一步摸索出合理的设计思维,用递归下降解析表达式为一个个对象。</p><p>第二单元相对设计思维难度没那么大,设计的是状态机模型,为每一个状态转移写相应的方法,弄清状态转移的条件,当时我甚至觉得这和面向对象关系不大,难点应该在并行过程中的信号量操作。</p><p>第三单元JML规格化设计确实能够带来很多好处。尽管JML语言的描述可能有些复杂,但是通过注释和规格化设计,写代码十分便利,并且方法与方法之间甚至可以不用考虑类与类之间的关系就能进行编码,同时也体会到规格化设计的好处。但是算法优化是这单元的难点,规格化设计只能保证可靠性但不能保证复杂度在一个很小的范围。</p><p>第四单元图书馆管理系统让我又一次体会到了面向对象和面向过程的不同,同样的需求放在面向过程是难以实现的,但设计架构时候抽象类,做到高内聚低耦合,能为编码和找bug提供便利。</p><h2 id="四个单元中测试思维的演进"><a href="#四个单元中测试思维的演进" class="headerlink" title="四个单元中测试思维的演进"></a>四个单元中测试思维的演进</h2><p>在前两个单元,我主要通过手动构造数据来进行测试。我会尝试构造极端的数据以及按照指导书中的具体要求有针对性地构造数据。这种方法帮助我检查系统在极端情况下的表现,并满足特定需求的测试要求。在第三个单元,我开始学习并尝试使用评测机进行测试。第四个单元,我的测试思路开始拓宽。我的测试方法不仅包括使用评测机进行全面和有针对性的测试,还包括手动构造极端样例。我在测试过程中综合运用这些方法。</p><h2 id="课程收获"><a href="#课程收获" class="headerlink" title="课程收获"></a>课程收获</h2><p>收获颇丰,一两句说不完,也很难描述。我学会了抽象和模块化思维,能够更好地划分问题,并设计适合的类和对象来解决它们。这种思维方式让我能够更好地组织和编写代码,提高了代码的可扩展性。在1,4单元中我也了解了设计模式和软件架构的原则,在第2单元中我了解并简单尝试了多线程的程序设计。这些让我能够在设计大型软件系统时有所依据,提高了代码的结构和可管理性。总的来说,OO课程让我能够以更加高效和优雅的方式编写代码。这些知识和技能将对我的软件开发之旅产生长远的影响。</p>]]></content>
</entry>
<entry>
<title>BUAA-OO-第三单元JML系列</title>
<link href="/2023/06/30/BUAA-OO-%E7%AC%AC%E4%B8%89%E5%8D%95%E5%85%83JML%E7%B3%BB%E5%88%97/"/>
<url>/2023/06/30/BUAA-OO-%E7%AC%AC%E4%B8%89%E5%8D%95%E5%85%83JML%E7%B3%BB%E5%88%97/</url>
<content type="html"><![CDATA[<h1 id="BUAA-OO-第三单元JML系列"><a href="#BUAA-OO-第三单元JML系列" class="headerlink" title="BUAA-OO-第三单元JML系列"></a>BUAA-OO-第三单元JML系列</h1><h2 id="1、架构设计"><a href="#1、架构设计" class="headerlink" title="1、架构设计"></a>1、架构设计</h2><p>每次作业除了一般规格的直接翻译为代码的方法以外,会有几个需要我们关注方法性能的函数。HW9的qts和qbs我是通过建立并且动态维护并查集来提升性能的。HW10的新增删边操作要对并查集进行适时重新建立来保证完备性,同时对于qba和qcs需要动态维护bestAcqutaince在person的属性中。HW11的qlm操作需要求最小环,我使用的是朴素的djiskra算法,删去一条边之后询问两个点之间是否还有路径,若有则为环,求最小环长,<del>虽然最后还是超时了</del>。</p><h3 id="图模型构建和维护"><a href="#图模型构建和维护" class="headerlink" title="图模型构建和维护"></a>图模型构建和维护</h3><p>对于求最小环的过程并没有创建和维护图,当需要查询权值时候直接通过queryValue函数返回这条边的权值。对于查询两个person之前是否认识时,采用了并查集(数组形式)。并查集的维护只需要在addRealation或者modifyRelation时候进行加边或者删去边即可维护认识的三角关系个数,同时在addPerson增加连通块个数即可维护连通块个数。</p><h2 id="2、本单元的测试过程"><a href="#2、本单元的测试过程" class="headerlink" title="2、本单元的测试过程"></a>2、本单元的测试过程</h2><h3 id="黑箱测试、白箱测试的理解"><a href="#黑箱测试、白箱测试的理解" class="headerlink" title="黑箱测试、白箱测试的理解"></a>黑箱测试、白箱测试的理解</h3><h4 id="黑箱测试"><a href="#黑箱测试" class="headerlink" title="黑箱测试"></a>黑箱测试</h4><p>测试人员只关注软件系统的输入和输出,而不考虑系统的内部结构和实现细节。测试人员没有访问或了解软件的内部代码、算法或设计。黑箱测试主要关注系统对于各种输入的响应和输出的正确性,以及系统是否满足预期的功能需求和规格说明。</p><p>优点包括:</p><ul><li>不需要了解系统内部的实现细节,可以由非开发人员执行。</li><li>从用户的角度出发,测试系统的实际行为。</li></ul><p>缺点包括:</p><ul><li>对于复杂的系统,测试覆盖面可能不够全面。</li><li>无法直接测试系统的内部逻辑和算法。</li></ul><h4 id="白箱测试"><a href="#白箱测试" class="headerlink" title="白箱测试"></a>白箱测试</h4><p>白箱测试主要关注系统的内部逻辑、控制流程、数据流和错误处理。测试人员通过测试系统的每个分支、路径和语句,以验证其正确性和健壮性。</p><p>优点包括:</p><ul><li>可以发现隐藏的错误、漏洞和性能问题。</li><li>可以提供关于系统代码质量和结构的反馈。</li></ul><p>缺点包括:</p><ul><li>需要测试人员具备深入的编程和系统理解能力。</li><li>可能需要更多的时间和资源。</li></ul><h3 id="单元测试、功能测试、集成测试、压力测试、回归测试"><a href="#单元测试、功能测试、集成测试、压力测试、回归测试" class="headerlink" title="单元测试、功能测试、集成测试、压力测试、回归测试"></a>单元测试、功能测试、集成测试、压力测试、回归测试</h3><ul><li>单元测试:针对最小的可测试单元,验证其功能是否符合预期。</li><li>功能测试:全面测试软件系统的功能,确保按照规格和需求正常运行。</li><li>集成测试:测试多个模块或组件之间的接口和交互是否正常工作。</li><li>压力测试:评估系统在正常和高负载条件下的性能和稳定性。</li><li>回归测试:在软件更改后执行的测试,确保已有功能和系统部分仍然正常工作。</li></ul><h3 id="测试工具"><a href="#测试工具" class="headerlink" title="测试工具"></a>测试工具</h3><p>我使用了讨论区同学的测试工具稍微修改后(修改出现的指令比例)和同学进行对拍。数据构造策略为使用随机构造方式和单一指令压力测试。</p><h2 id="性能问题及其修复情况"><a href="#性能问题及其修复情况" class="headerlink" title="性能问题及其修复情况"></a>性能问题及其修复情况</h2><p>HW10出现的qba和qcs的ctle是因为没有动态维护bestAcupuncture,修复了就过了。HW11出现的qlm超时,是因为djiskra算法时间复杂度还是过大,我看了一下,strong5在评测机上只能大概跑一半的数据就ctle了。修复方法是在算法基础上进行堆优化后可以大大减少时间复杂度。</p><h2 id="规格与实现分离"><a href="#规格与实现分离" class="headerlink" title="规格与实现分离"></a>规格与实现分离</h2><p>规格与实现分离是软件开发中的原则,将系统的需求和功能描述与实际的代码实现分开。规格描述了系统应该具有的功能和行为,而实现是开发人员编写的代码。分离规格和实现有助于明确责任分工、提高可维护性和可测试性,以及支持系统的修改和扩展。好处包括:</p><ol><li>清晰的分工:规格由业务分析师或产品经理编写,开发人员负责实现代码。这种分离让企业中的人分工合作效率高。</li><li>可维护性和可扩展性:规格的分离使得对系统的修改和扩展更加容易。通过修改规格而不是直接修改实现代码,可以减少对现有代码的影响,降低引入新问题的风险。</li><li>高效的测试和验证:通过规格的定义可以更容易地编写相应的测试用例来验证系统是否满足规格要求。</li><li>可重用性:规格的分离使得方法的功能和行为可以被重复使用。</li></ol><h2 id="OKTest"><a href="#OKTest" class="headerlink" title="OKTest"></a>OKTest</h2><p>作用包括:</p><ul><li>我们可以编写详细准确的规格说明,覆盖系统各方面需求。</li><li>测试者可以设计明确的测试用例,包括各种输入和预期输出。</li><li>测试者使用自动化测试工具来执行OK测试,提高效率和准确性。</li><li>使用静态代码分析工具检查代码规范性和潜在错误。</li><li>结合其他测试方法,如单元测试、功能测试和集成测试。</li></ul><p>建议设计人员要确保规格明确,测试人员要使用多种测试方法,自动化测试,关注边界条件和异常情况,定期审查和更新规格,以及持续学习和改进。这可以帮助提高OK测试的质量和效率,确保代码实现与规格的一致性。</p><h2 id="学习体会"><a href="#学习体会" class="headerlink" title="学习体会"></a>学习体会</h2><p>算法可真难,算法可真难,算法可真难。前两次作业现学了并查集当时觉得并查集可真好用,也好理解,觉得这单元难度也不是很高,但是第三次作业的寻找最小环当朴素的最短路径算法的时间复杂度都能达到o(n^3)甚至o(n^4),我便开始找新的算法,但是越来越迷茫了,还在最后还是完成了,但经历了许许多多的波折。虽然抱怨了这么多,但是算法一直都是我们学计算机的必备的技能(<del>吃饭的家伙</del>)。除此之外这单元教会了我们如何阅读规格,如何写规格。虽然<del>写规格很难</del>,但是这种严谨的开发流程是应该被应用的。</p>]]></content>
</entry>
<entry>
<title>BUAA-OO-第二单元模拟多线程实时电梯系统</title>
<link href="/2023/04/13/2023-04-13-BUAA-OO-%E7%AC%AC%E4%BA%8C%E5%8D%95%E5%85%83%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E6%97%B6%E7%94%B5%E6%A2%AF%E7%B3%BB%E7%BB%9F/"/>
<url>/2023/04/13/2023-04-13-BUAA-OO-%E7%AC%AC%E4%BA%8C%E5%8D%95%E5%85%83%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E6%97%B6%E7%94%B5%E6%A2%AF%E7%B3%BB%E7%BB%9F/</url>
<content type="html"><![CDATA[<h1 id="BUAA-OO-第二单元模拟多线程实时电梯系统"><a href="#BUAA-OO-第二单元模拟多线程实时电梯系统" class="headerlink" title="BUAA-OO-第二单元模拟多线程实时电梯系统"></a>BUAA-OO-第二单元模拟多线程实时电梯系统</h1><img src="/2023/04/13/2023-04-13-BUAA-OO-%E7%AC%AC%E4%BA%8C%E5%8D%95%E5%85%83%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E6%97%B6%E7%94%B5%E6%A2%AF%E7%B3%BB%E7%BB%9F/0.jpeg" class title="P0"><p>先贴类图</p><h2 id="HW5"><a href="#HW5" class="headerlink" title="HW5"></a>HW5</h2><img src="/2023/04/13/2023-04-13-BUAA-OO-%E7%AC%AC%E4%BA%8C%E5%8D%95%E5%85%83%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E6%97%B6%E7%94%B5%E6%A2%AF%E7%B3%BB%E7%BB%9F/5.png" class title="P1"><h2 id="HW6"><a href="#HW6" class="headerlink" title="HW6"></a>HW6</h2><img src="/2023/04/13/2023-04-13-BUAA-OO-%E7%AC%AC%E4%BA%8C%E5%8D%95%E5%85%83%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E6%97%B6%E7%94%B5%E6%A2%AF%E7%B3%BB%E7%BB%9F/6.png" class title="P2"><h2 id="HW7"><a href="#HW7" class="headerlink" title="HW7"></a>HW7</h2><img src="/2023/04/13/2023-04-13-BUAA-OO-%E7%AC%AC%E4%BA%8C%E5%8D%95%E5%85%83%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E6%97%B6%E7%94%B5%E6%A2%AF%E7%B3%BB%E7%BB%9F/7.png" class title="P3"><h2 id="线程协作图"><a href="#线程协作图" class="headerlink" title="线程协作图"></a>线程协作图</h2><img src="/2023/04/13/2023-04-13-BUAA-OO-%E7%AC%AC%E4%BA%8C%E5%8D%95%E5%85%83%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E6%97%B6%E7%94%B5%E6%A2%AF%E7%B3%BB%E7%BB%9F/1.png" class title="P4"><h2 id="1-同步块的设置和锁的选择,锁与同步块中处理语句之间的关系"><a href="#1-同步块的设置和锁的选择,锁与同步块中处理语句之间的关系" class="headerlink" title="1.同步块的设置和锁的选择,锁与同步块中处理语句之间的关系"></a>1.同步块的设置和锁的选择,锁与同步块中处理语句之间的关系</h2><ul><li>三次作业均为synchronized作为锁来实现同步互斥,至于锁和同步块之间的关系就是在一个同步块中,所有的语句都是受到同一个锁的保护的,这意味着只有拥有该锁的线程才能够执行同步块中的代码。因此,在同步块中的处理语句需要获取锁之后才能执行,如果在同步块中的某个处理语句执行过程中失去了锁,则其他线程有可能会抢占该锁并访问共享资源,从而导致线程安全问题。Platform作为共享对象,为了保证线程安全对其所有的方法用synchronized修饰。</li></ul><h2 id="2-三次作业中的调度器设计,调度器如何与程序中的线程进行交互"><a href="#2-三次作业中的调度器设计,调度器如何与程序中的线程进行交互" class="headerlink" title="2.三次作业中的调度器设计,调度器如何与程序中的线程进行交互"></a>2.三次作业中的调度器设计,调度器如何与程序中的线程进行交互</h2><h3 id="HW5-1"><a href="#HW5-1" class="headerlink" title="HW5"></a>HW5</h3><p>第一次作业使用了生产者-消费者模型,InputThread为生产者,Elevator为消费者,Platform为中间的channel,调度器为Schedule,从线程协作图可以看出第一次作业仅仅需要MainClass创建初始Elevator,并开启Elevator线程,开启InputThread和Schedule线程。InputThread产生Passenger给总的Platform,由Schedule进行调度,分配给每个电梯自己的Platform,第一次作业我的调度策略是分配给当前电梯Platform含有的Passenger最少的。调度器与Platform进行交互,负责放置Passenger,使用put()函数。Platform作为共享对象,为了保证线程安全对其所有的方法用synchronized修饰。最后每个Elevator就是一个消费者,从自己的Platform取Passenger,包括中途捎带也是对自己的Platform来说的。</p><h3 id="HW6-1"><a href="#HW6-1" class="headerlink" title="HW6"></a>HW6</h3><p>第二次作业在第一次作业的基础上增加了ADD和MAINTAIN电梯的要求,这在第一次作业的架构上只需要在InputThread里面新增需求进行相应处理就行了。ADD电梯可以new一个Platform给一个new出来的电梯,将Platform接受调度器的分配,再开启电梯的线程就完成了。MAINTAIN电梯只需要先把Platform设置为不接受调度器的分配,如何电梯收到信号,就近将电梯里的乘客修改From放到总Platform接受调度器的重新分配,电梯Platform里的乘客只需要放回总Platform接受重新分配即可。</p><h3 id="HW7-1"><a href="#HW7-1" class="headerlink" title="HW7"></a>HW7</h3><p>第三次作业在原有的架构上只需要为电梯设置每个楼层的可达性,在开门前进行判断,当电梯进行维护时需要特判。同时因为电梯不是每个楼层均可到达,要为乘客规划前往的路线,用数组储存乘客途径的楼层,至少保证数组第一个元素的准确性,其余的可以等乘客中途下电梯后重新进行规划。规划路线用的是Floyds算法,用Path记录途经楼层,使用算法前需要为楼层建图,使用的是邻接矩阵。作业还新增要求在每个楼层对开着门的电梯数和只接人的电梯数做出限定,我的处理方法是在开门条件新增判断,统计当前楼层处于这两种状态的电梯数,如果不满足则继续等待,等待到满足条件为止。相应的在电梯开关门和上下人时候要设置相应的值,来方便统计这两种状态。</p><h3 id="三次作业稳定的内容和易变的内容"><a href="#三次作业稳定的内容和易变的内容" class="headerlink" title="三次作业稳定的内容和易变的内容"></a>三次作业稳定的内容和易变的内容</h3><h4 id="稳定内容"><a href="#稳定内容" class="headerlink" title="稳定内容"></a>稳定内容</h4><ul><li>生产者消费者模型</li><li>调度器都是分配给电梯自己的Platform</li><li>电梯的运行行为</li><li>共享对象Platform的同步互斥</li></ul><h4 id="易变内容"><a href="#易变内容" class="headerlink" title="易变内容"></a>易变内容</h4><ul><li>电梯的开门条件判断</li><li>电梯MAINTAIN后状态的改变</li><li>调度器分配策略</li></ul><h2 id="3-bug分析"><a href="#3-bug分析" class="headerlink" title="3.bug分析"></a>3.bug分析</h2><p>HW6的bug在于要为每个电梯的Platform设置一个限度,不然当某一时刻请求量巨大,调度器完成分配后,新增电梯,但是由于已经完成分配,这些新增的电梯犹如摆设,会影响运行时间。解决方法就是为Platform设置限额,超过限额调度器就进行等待,等待新电梯的加入或者是Platform乘客的减少。debug方法为观察输出,进行推理,然后为新增的电梯的Platform设置println,获取乘客,发现为0,找到了问题。</p><p>HW7的bug在于MAINTAIN后也要符合每个楼层开门和只接人电梯数的限定,只需要增加特判即可。debug方法依然是通过观察输出,在关键处设置println来获取状态。由于多线程的缘故,用断点单步调试效果很差。</p><h2 id="4-心得体会"><a href="#4-心得体会" class="headerlink" title="4.心得体会"></a>4.心得体会</h2><ul><li>学会了生产者消费者模式,这些设计模式十分高效,做到了SOLID设计原则,高内聚低耦合十分适合移植使用,并且也十分形象。</li><li>多线程的安全问题永远值得我们取谨慎对待,尤其是在共享对象的处理上,可以先对所有的方法加上锁修饰,最后再去掉只读不写的方法的锁修饰。wait与notifyAll的使用也十分形象,但不能滥用notifyAll。</li><li>乘客的输入,调度器的设计,电梯的运行,这些模块各司其职,让我想到了流水线,只需要保证每个模块的正确性,在需求叠加时候只需要修改某几个模块,而不是“动一发而牵全身”,层次化的设计有助于迭代。</li><li>这单元的工作量相比于上个单元少了许多,代码量就能看出来,但是相应需要学习的知识却不少,学到了许多。</li></ul>]]></content>
</entry>
<entry>
<title>OS-Lab2</title>
<link href="/2023/04/04/OS-Lab2/"/>
<url>/2023/04/04/OS-Lab2/</url>
<content type="html"><![CDATA[<h1 id="Lab2实验报告"><a href="#Lab2实验报告" class="headerlink" title="Lab2实验报告"></a>Lab2实验报告</h1><h2 id="Thinkings"><a href="#Thinkings" class="headerlink" title="Thinkings"></a>Thinkings</h2><h3 id="Thingking2-1"><a href="#Thingking2-1" class="headerlink" title="Thingking2.1"></a>Thingking2.1</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">在编写的 C 程序中,指针变量中存储的地址是虚拟地址,还是物理地址?MIPS 汇编程序中 lw 和 sw 使用的是虚拟地址,还是物理地址? </span><br></pre></td></tr></table></figure><p>均为虚拟地址,凡是用户编写的程序均使用的是虚拟地址,只有操作系统能直接与物理地址接触,用户与物理地址是不透明的。</p><h3 id="Thinking2-2"><a href="#Thinking2-2" class="headerlink" title="Thinking2.2"></a>Thinking2.2</h3><figure class="highlight plaintext"><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><p>修改时只需要修改宏定义,而不是多处修改,而且宏定义可读性强。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">查看实验环境中的 /usr/include/sys/queue.h,了解其中单向链表与循环链表的实现,比较它们与本实验中使用的双向链表,分析三者在插入与删除操作上的性能差异。</span><br></pre></td></tr></table></figure><p>实验中list是双向循环列表,结构体包含指向后一个节点的指针和指向前一个节点的后一个节点的指针的指针。tail为单向链表,包含tqh_first和tqh_last。</p><ul><li>单向链表的插入操作的时间复杂度为O(1),删除操作的时间复杂度为O(n)。</li><li>循环链表的插入操作的时间复杂度为O(1),删除操作的时间复杂度为O(n)。</li><li>双向链表的插入操作的时间复杂度为O(1),删除操作的时间复杂度为O(1)。</li></ul><h3 id="Thinking2-3"><a href="#Thinking2-3" class="headerlink" title="Thinking2.3"></a>Thinking2.3</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">请阅读 include/queue.h 以及 include/pmap.h, 将 Page_list 的结构梳理清楚,选择正确的展开结构。</span><br></pre></td></tr></table></figure><p>观察pamp.h</p><img src="/2023/04/04/OS-Lab2/11.png" class title="P11"><img src="/2023/04/04/OS-Lab2/12.png" class title="P12"><p>把Page_list,Page代入宏LIST_HEAD,把Page代入宏LIST_ENTRY</p><img src="/2023/04/04/OS-Lab2/14.png" class title="P14"><p>最后将Page_list中的struct Page替换得答案,最后应该选c</p><img src="/2023/04/04/OS-Lab2/15.png" class title="P15"><h3 id="Thinking2-4"><a href="#Thinking2-4" class="headerlink" title="Thinking2.4"></a>Thinking2.4</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">请阅读上面有关 R3000-TLB 的叙述,从虚拟内存的实现角度,阐述 ASID 的必要性</span><br></pre></td></tr></table></figure><p><em><strong>ASID:Address Space IDentifier</strong></em> 用于区分不同的地址空间。查找 TLB 表项时,除了需要提供 VPN,还需要提供 ASID(同一虚拟地址在不同的地址空间中通常映射到不同的物理地址)。使用asid表明TLB中VPN的”身份”,可以在查TLB快表时候验证这个地址映射是否合法,若不合法及时中断可以达到数据保护的作用。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">请阅读《IDT R30xx Family Software Reference Manual》的 Chapter 6,结合 ASID段的位数,说明 R3000 中可容纳不同的地址空间的最大数量。</span><br></pre></td></tr></table></figure><p>ASID 为11-6位,共6位,容纳不同地址空间的最大数量为64(2^6)。</p><h3 id="Thinking2-5"><a href="#Thinking2-5" class="headerlink" title="Thinking2.5"></a>Thinking2.5</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">• tlb_invalidate 和 tlb_out 的调用关系?</span><br></pre></td></tr></table></figure><p><code>tlb_invalidate</code> 调用了<code>tlb_out</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">• 请用一句话概括 tlb_invalidate 的作用。</span><br></pre></td></tr></table></figure><p>Flush TLB。更新TLB表项。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">逐行解释 tlb_out 中的汇编代码。</span><br></pre></td></tr></table></figure><img src="/2023/04/04/OS-Lab2/2.png" class title="P2"><h3 id="Thinking2-6"><a href="#Thinking2-6" class="headerlink" title="Thinking2.6"></a>Thinking2.6</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">• 简单了解并叙述 X86 体系结构中的内存管理机制,比较 X86 和 MIPS 在内存管理上的区别。</span><br></pre></td></tr></table></figure><p>X86在TLB不命中时,MMU以CR3为页目录基址,转换得到物理页框号,MMU会填充TLB加快下次转换速度。<br>MIPS会触发TLB异常,内核的_do_tlb_refill会以导致缺页异常的虚拟地址查询页表,获得转换失败的物理页框号,并将其填入TLB。</p><h1 id="难点分析"><a href="#难点分析" class="headerlink" title="难点分析"></a>难点分析</h1><ul><li><p>要时时刻刻区分哪些是物理地址,哪些是虚拟地址</p></li><li><p>明确定义的函数的作用,比如PADDR和KADDR的区别</p></li><li><p>对于链表的掌握还不足,做链表填空花费了很多时间。</p></li><li><p>另外,理解Page结构体的结构,是本次实验比较大的一个难点。</p><img src="/2023/04/04/OS-Lab2/3.png" class title="P3"></li><li><p>两级页表的结构要清晰。</p><img src="/2023/04/04/OS-Lab2/6.png" class title="P6"></li><li><p>页目录自映射。</p></li><li><p>充填TLB的汇编代码</p></li></ul><h1 id="思考与体会"><a href="#思考与体会" class="headerlink" title="思考与体会"></a>思考与体会</h1><p>lab2补充了理论课,解决了部分理论课给我带来的困惑,尤其是听了理论课内存管理后,感觉似懂非懂,不知道自己是否真正掌握。遇到的第一个坎首先是Page结构体的理解,这个结构体不同于以前遇到的,指针的指针让我很不适应,实验后又重学了遍链表知识。第二个坎是page开头函数中对于物理地址与虚拟地址的混淆,准确来说是时而知道,时而模糊。还有就是TLB表项的概念,asid以及TLB汇编部分花了一定的时间去理解,最后终于看懂了指导书以及代码所描述的行为。但是关于TLB使用的汇编代码不是很熟练,并且对各个CP0寄存器的功能也不熟,希望能在lab3加深理解。</p>]]></content>
</entry>
<entry>
<title>OS-Lab1</title>
<link href="/2023/04/04/OS-Lab1/"/>
<url>/2023/04/04/OS-Lab1/</url>
<content type="html"><![CDATA[<h1 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h1><h2 id="Thinking-1-1"><a href="#Thinking-1-1" class="headerlink" title="Thinking 1.1"></a>Thinking 1.1</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">请阅读附录中的编译链接详解,尝试分别使用实验环境中的原生 x86 工具链(gcc、ld、readel f、objdump 等)和 MIPS 交叉编译工具链(带有 mips-linux-gnu前缀),重复其中的编译和解析过程,观察相应的结果,并解释其中向 objdump 传入的参数的含义。</span><br></pre></td></tr></table></figure><p><code>objdump</code>中参数:</p><p><code>-D</code>:从objfile中反汇编所有的section;</p><p><code>-S</code>:反汇编出源代码,与-g一起使用效果明显。</p><p>源代码在与预处理环节仅仅会进行字符串替换,在编译阶段对于不能确定地址的函数,但是又使用extern修饰的函数用一串0代替地址,只有在链接器将目标文件链接时,才会对那些函数地址进行填充,最后生成可执行文件。</p><h2 id="Thinking-1-2"><a href="#Thinking-1-2" class="headerlink" title="Thinking 1.2"></a>Thinking 1.2</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">尝试使用我们编写的 readelf 程序,解析之前在 target 目录下生成的内核 ELF 文件。</span><br></pre></td></tr></table></figure><img src="/2023/04/04/OS-Lab1/1.png" class title="P1"><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">也许你会发现我们编写的 readelf 程序是不能解析 readelf 文件本身的,而我们刚才介绍的系统工具 readelf 则可以解析,这是为什么呢?(提示:尝试使用 readelf-h,并阅读 tools/readelf 目录下的 Makefile,观察 readelf 与 hello 的不同)</span><br></pre></td></tr></table></figure><h3 id="hello"><a href="#hello" class="headerlink" title="hello"></a>hello</h3><img src="/2023/04/04/OS-Lab1/2.png" class title="P2"><h3 id="readelf"><a href="#readelf" class="headerlink" title="readelf"></a>readelf</h3><img src="/2023/04/04/OS-Lab1/3.png" class title="P3"><p>因为readelf是64位的elf文件,而hello是32位的elf文件。格式不同,而我们编写的readelf只能解析32位elf文件。</p><h2 id="Thinking-1-3"><a href="#Thinking-1-3" class="headerlink" title="Thinking 1.3"></a>Thinking 1.3</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">在理论课上我们了解到,MIPS 体系结构上电时,启动入口地址为 0xBFC00000(其实启动入口地址是根据具体型号而定的,由硬件逻辑确定,也有可能不是这个地址,但一定是一个确定的地址),但实验操作系统的内核入口并没有放在上电启动地址,而是按照内存布局图放置。思考为什么这样放置内核还能保证内核入口被正确跳转到?(提示:思考实验中启动过程的两阶段分别由谁执行。) </span><br></pre></td></tr></table></figure><p>操作系统启动时,首先执行bootloader,在stage1阶段初始化硬件设备。MIPS处理器来说,MIPS体系结构上电时,启动入口的地址为某一个确定的地址,将这个虚拟地址的高三位清零之后的物理地址放置着开始MIPS的第一条指令,之后进入stage2阶段,BIOS从MBR中读取开机信息,然后将文件系统中的内核镜像文件加载到内存中,最后跳到指定的内核入口。</p><p>而我们的实验省去了bootloader,因此只需要将kernel镜像文件放置在正确的物理地址上,通过start.s跳转到正确的内核文件存放地址,之后就能安全执行。</p><h1 id="难点分析"><a href="#难点分析" class="headerlink" title="难点分析"></a>难点分析</h1><h2 id="Exercise-1"><a href="#Exercise-1" class="headerlink" title="Exercise 1"></a>Exercise 1</h2><ul><li>要了解ELF文件在编译和运行两种视角下的不同结构,节头表与段头表只是不同的展现视角,内容是一样的。</li><li>要理解ELF文件之中的三个结构体并利用文件头地址和偏移量求地址</li><li>我实验过程中比较困难的地方是没有读懂节头表结构体内数据的定义,一直在思考elf头结构体中的属性定义。</li></ul><h2 id="Exercise-2"><a href="#Exercise-2" class="headerlink" title="Exercise 2"></a>Exercise 2</h2><ul><li>注意是按照%[flags][width][length]<specifier>这样的顺序进行的,每次循环当作扫描到%处理,要重点理解代码中出现的函数的作用,搞清楚重要变量的作用,直到读到’\0’后才结束循环。注意要将padc,ladjust等标志作用的变量每循环一次都要清零,尤其是padc初值为’ ’,因为循环一次就表示读取并处理了一次%[flags][width][length]<specifier>形式。</li><li>处理可变参数里面数字是复数的情况,要传入负数的绝对值,再把neg_flag置为1。</li></ul><h1 id="思考与体会"><a href="#思考与体会" class="headerlink" title="思考与体会"></a>思考与体会</h1><p>lab1补充了理论课,解决了部分理论课给我带来的困惑,比如elf文件相关的知识,因为对这个知识点来说比较陌生,所以花费了不少的时间去理解。而第二个exercise的printk是考察大一C语言指针的使用,个人觉得与操作系统关系不大,加上注释写的比较详细,因此完成也不困难。但是变长参数的形式让我对printf等类似库函数有了进一步的理解,以前一直觉得库函数与我们自己写的函数是不一样的,因为我们自己写的函数要指定参数的个数与类型,而printf不需要,现在知道了可边长参数这一概念以后觉得豁然开朗。</p><p>这次实验也学到了很多,第一是关于Makefile的使用以及make和make clean的操作;第二是关于内核地址与MIPS的结合,.data和.text,学的不是很明白,经过lab1的学习明朗了很多,毕竟这俩在上学期的计组实验是频繁出现的,只是当时没有深入挖掘其含义。</p>]]></content>
</entry>
<entry>
<title>OS-Lab0</title>
<link href="/2023/04/04/OS-Lab0/"/>
<url>/2023/04/04/OS-Lab0/</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>BUAA-OO 第一单元表达式解析</title>
<link href="/2023/03/15/2023-03-15-BUAA-OO-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%A7%A3%E6%9E%90/"/>
<url>/2023/03/15/2023-03-15-BUAA-OO-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%A7%A3%E6%9E%90/</url>
<content type="html"><![CDATA[<h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><img src="/2023/03/15/2023-03-15-BUAA-OO-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%A7%A3%E6%9E%90/problem.jpeg" class title="Problem"><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><p>当我拿到第一次作业的时候,我不知所措,一开始不知道如何解析字符串,当理解了递归下降解析文法后,依然无法找到一种合适的方法去存储最基本的项,做不到最基础的(x+1)*(y+2)的计算,还记得那一次作业是在周六晚上才完成的,其中经历了许许多多的重构与折磨。OO要改变的是思维方式,这个过程是很漫长的,有很多方面都不适应,做出的重构就是思路的改变。仅以本博客记录记录我第一单元的经历。</p><h2 id="二、第一单元结构"><a href="#二、第一单元结构" class="headerlink" title="二、第一单元结构"></a>二、第一单元结构</h2><p><strong>2.1 HW1</strong></p><p><strong>2.1.1 设计思路</strong></p><ul><li>采用梯度下降的解析方法(<code>Paser</code>和<code>Lexer</code>)对表达式进行解析,整个表达式分为3层:<code>Expr</code>、<code>Term</code>、<code>Factor</code>,而Factor又包括<code>Number</code>、<code>Power</code>(幂函数)、<code>Expr</code>。</li><li>创建Poly类表示经过文法解析后的Expr,Term,Factor代表的数学多项式含义,Poly有一个Hashmap属性,Special储存了x,y,z的指数,通过将Special作为key映射到不同的value(系数)来储存多项式。通过createPoly()函数可以实例化一个Poly对象。</li><li>同时设置Power,Number类来表示最底层的类,这两个类能直接生成Poly类,而不需要递归调用内部因子的createPoly()函数。Expr生成Poly需要对组成Expr的每个Term调用createPoly()函数,最终把所有的Poly相加(addPoly())或相减(subPoly())</li><li>Term类生成Poly需要调用组成Term的Expr,Power,Number的createPoly()函数,而最终会递归到Power,Number,而这两个类是可以直接生成Poly的,因此不会进入无穷递归。最后把生成的Poly相乘,调用mulPoly()函数完成。</li><li>最后一步,在main里依次调用输入的字符串的递归下降解析,生成了一个Expr对象,再调用Expr的createPoly()方法,就能得到一个Poly的Hashmap,化简输出即完成了第一次作业。</li></ul><p><strong>2.1.2 UML类图</strong></p><img src="/2023/03/15/2023-03-15-BUAA-OO-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%A7%A3%E6%9E%90/1.png" class title="P1"><p><strong>2.1.3 类复杂度</strong></p><h4 id="OCavg-Average-opearation-complexity(平均操作复杂度)"><a href="#OCavg-Average-opearation-complexity(平均操作复杂度)" class="headerlink" title="OCavg = Average opearation complexity(平均操作复杂度)"></a><code>OCavg</code> = <code>Average opearation complexity</code>(平均操作复杂度)</h4><h4 id="OCmax-Maximum-operation-complexity(最大操作复杂度)"><a href="#OCmax-Maximum-operation-complexity(最大操作复杂度)" class="headerlink" title="OCmax = Maximum operation complexity(最大操作复杂度)"></a><code>OCmax</code> = <code>Maximum operation complexity</code>(最大操作复杂度)</h4><h4 id="WMC-Weighted-method-complexity(加权方法复杂度)"><a href="#WMC-Weighted-method-complexity(加权方法复杂度)" class="headerlink" title="WMC = Weighted method complexity(加权方法复杂度)"></a><code>WMC</code> = <code>Weighted method complexity</code>(加权方法复杂度)</h4><table><thead><tr><th>Class</th><th>OCavg</th><th>OCmax</th><th>WMC</th></tr></thead><tbody><tr><td>Expr</td><td>3.5</td><td>9</td><td>14</td></tr><tr><td>Lexer</td><td>2</td><td>5</td><td>10</td></tr><tr><td>MainClass</td><td>1</td><td>1</td><td>2</td></tr><tr><td>Number</td><td>1</td><td>1</td><td>3</td></tr><tr><td>Parser</td><td>5.25</td><td>8</td><td>21</td></tr><tr><td>Poly</td><td>2.43</td><td>5</td><td>17</td></tr><tr><td>Power</td><td>2.5</td><td>4</td><td>5</td></tr><tr><td>Special</td><td>1</td><td>1</td><td>7</td></tr><tr><td>Term</td><td>2.33</td><td>5</td><td>7</td></tr></tbody></table><p>从图中可以看出<code>Paser</code>和<code>Poly</code>的复杂度较高,这主要是因为程序可以分为解析和计算两大主要部分。解析部分主要由<code>Paser</code>实现,计算部分主要由<code>Poly</code>实现,承担大部分工作而造成的复杂度增加。</p><p><strong>2.1.4 方法复杂度</strong></p><p><strong><code>CogC</code> = <code>Cognitive complexity</code>(认知复杂度)</strong></p><h4 id="ev-G-Essential-cyclomatic-complexity(基本圈复杂度)"><a href="#ev-G-Essential-cyclomatic-complexity(基本圈复杂度)" class="headerlink" title="ev(G) = Essential cyclomatic complexity(基本圈复杂度)"></a><code>ev(G)</code> = <code>Essential cyclomatic complexity</code>(基本圈复杂度)</h4><h4 id="iv-G-Design-complexity(设计复杂度)"><a href="#iv-G-Design-complexity(设计复杂度)" class="headerlink" title="iv(G) = Design complexity(设计复杂度)"></a><code>iv(G)</code> = <code>Design complexity</code>(设计复杂度)</h4><h4 id="v-G-cyclonmatic-complexity(圈复杂度)"><a href="#v-G-cyclonmatic-complexity(圈复杂度)" class="headerlink" title="v(G) = cyclonmatic complexity(圈复杂度)"></a><code>v(G)</code> = <code>cyclonmatic complexity</code>(圈复杂度)</h4><table><thead><tr><th>Method</th><th>CogC</th><th>ev(G)</th><th>iv(G)</th><th>v(G)</th></tr></thead><tbody><tr><td>Expr.Expr()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.addTerm(Term, String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.createPoly()</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Expr.print()</td><td>24</td><td>1</td><td>11</td><td>12</td></tr><tr><td>Lexer.Lexer(String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Lexer.behind()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Lexer.getNumber()</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Lexer.next()</td><td>10</td><td>2</td><td>7</td><td>9</td></tr><tr><td>Lexer.peek()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>MainClass.main(String[])</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>MainClass.simplify(String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.Number(BigInteger)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.addNumber(BigInteger)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.createPoly()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Parser.Parser(Lexer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Parser.parseExpr()</td><td>16</td><td>1</td><td>9</td><td>9</td></tr><tr><td>Parser.parseFactor()</td><td>9</td><td>5</td><td>6</td><td>6</td></tr><tr><td>Parser.parseTerm()</td><td>20</td><td>1</td><td>8</td><td>8</td></tr><tr><td>Poly.Poly()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.addPoly(Poly, Poly)</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.getPolys()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.merge()</td><td>8</td><td>1</td><td>5</td><td>5</td></tr><tr><td>Poly.mulPoly(Poly, Poly)</td><td>3</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.setPolys(HashMap<Special, BigInteger>)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.subPoly(Poly, Poly)</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Power.Power(Integer, char)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Power.createPoly()</td><td>3</td><td>1</td><td>4</td><td>4</td></tr><tr><td>Special.equals(Special)</td><td>1</td><td>1</td><td>1</td><td>3</td></tr><tr><td>Special.getX()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getY()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getZ()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setX(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setY(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setZ(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.Term()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.addFactor(Factor)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.createPoly()</td><td>5</td><td>1</td><td>5</td><td>5</td></tr></tbody></table><p>分析可知,<code>Expr.print()</code>和<code>Parser.parseTerm()</code>复杂度较高,解析项和因子情况比较复杂因此导致<code>Parser.parseTerm()</code>复杂度提升。而<code>Expr.print()</code>是因为我过程式编程思想没有完全摒弃导致的,在之后的迭代我将其优化为重写成各个类的<code>toString()</code>来输出结果。</p><p><strong>2.2 HW2</strong></p><p><strong>2.2.1 设计思路</strong></p><ul><li><h5 id="自定义函数"><a href="#自定义函数" class="headerlink" title="自定义函数"></a>自定义函数</h5><p>我选择在解析表达式之前建Func类来存自定义函数替换规则,之后在递归解析表达式过程中遇到自定义函数用字符串替换的方式去除自定义函数,返回表达式因子,为了保证运算优先级,替换后的因子加括号。比如,</p><figure class="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></pre></td><td class="code"><pre><span class="line">1</span><br><span class="line">f(x,y)=x*y (1)</span><br><span class="line">f(y,z) (2)</span><br><span class="line">替换后 (y)*(z)</span><br></pre></td></tr></table></figure><p>但需要注意的是直接使用String的repalce会出现因为替换有先后顺序,会导致后一个替换<strong>错误替换</strong>前一个替换,例如上面例子,先把x->y,再把y->z,会先后得到(y)<em>y和((z))</em>(z)。因此可以为(2)式找一个与(1)式子没有交集的“自变量”,比如{h,j,k}来先替换(2)式中的{x,y,z},最后完成因子替换后再换回。 <strong>注意,找无关变量不能太随意,比如找{a,b,c},可能就会替换“cos”中的 ‘c’。</strong></p></li><li><h5 id="三角函数"><a href="#三角函数" class="headerlink" title="三角函数"></a>三角函数</h5><p>我选择将其作为与幂函数,整数同一“层面”的类,接口为Factor。 相比于上一次作业的架构,只需要将Poly给改进一下,使其能储存系数,x,y,z指数,三角函数及其指数,具体形式如下,可以考虑单独建一个类存x,y,z指数,和Hashmap映射三角函数与指数,再将这个类映射系数。</p><img src="/2023/03/15/2023-03-15-BUAA-OO-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%A7%A3%E6%9E%90/4.png" class title="P4"></li></ul><p><strong>2.2.2 UML类图</strong></p><img src="/2023/03/15/2023-03-15-BUAA-OO-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%A7%A3%E6%9E%90/2.png" class title="P2"><p><strong>2.2.3 类复杂度</strong></p><table><thead><tr><th>Class</th><th>OCavg</th><th>OCmax</th><th>WMC</th></tr></thead><tbody><tr><td>Expr</td><td>1.5</td><td>3</td><td>6</td></tr><tr><td>Func</td><td>1.25</td><td>2</td><td>5</td></tr><tr><td>Lexer</td><td>2.75</td><td>7</td><td>11</td></tr><tr><td>MainClass</td><td>2.33</td><td>4</td><td>7</td></tr><tr><td>Number</td><td>1</td><td>1</td><td>4</td></tr><tr><td>Parser</td><td>6</td><td>11</td><td>30</td></tr><tr><td>Poly</td><td>4.38</td><td>14</td><td>57</td></tr><tr><td>Power</td><td>2.33</td><td>4</td><td>7</td></tr><tr><td>Special</td><td>1.17</td><td>3</td><td>14</td></tr><tr><td>Term</td><td>1.33</td><td>2</td><td>4</td></tr><tr><td>Triangle</td><td>1.67</td><td>2</td><td>5</td></tr></tbody></table><p>类新增了Triangle和Func类,复杂度均不高,因为只是新增了功能并不用承担很复杂的工作。由于调用是自顶向下的,最底层的Poly以及顶层的解析Parser这两个类的复杂度依然很高,这是架构设计导致的必然结果。</p><p><strong>2.2.4 方法复杂度</strong></p><table><thead><tr><th>Method</th><th>CogC</th><th>ev(G)</th><th>iv(G)</th><th>v(G)</th></tr></thead><tbody><tr><td>Expr.Expr()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.addTerm(Term, String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.createPoly()</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Expr.toString()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Func.Func(char, ArrayList<Character>, Expr)</Character></td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Func.change(ArrayList<Factor>)</Factor></td><td>1</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Func.getName()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Func.simplify(String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Lexer.Lexer(String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Lexer.getNumber()</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Lexer.next()</td><td>12</td><td>2</td><td>9</td><td>11</td></tr><tr><td>Lexer.peek()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>MainClass.createFunc(String)</td><td>6</td><td>1</td><td>4</td><td>4</td></tr><tr><td>MainClass.main(String[])</td><td>1</td><td>1</td><td>2</td><td>2</td></tr><tr><td>MainClass.simplify(String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.Number(BigInteger)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.addNumber(BigInteger)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.createPoly()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.toString()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Parser.Parser(Lexer, ArrayList<Func>)</Func></td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Parser.parseExpr()</td><td>16</td><td>1</td><td>9</td><td>9</td></tr><tr><td>Parser.parseFactor()</td><td>16</td><td>8</td><td>11</td><td>11</td></tr><tr><td>Parser.parseFunc()</td><td>4</td><td>3</td><td>4</td><td>4</td></tr><tr><td>Parser.parseTerm()</td><td>20</td><td>1</td><td>8</td><td>8</td></tr><tr><td>Poly.Poly()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.addPoly(Poly, Poly)</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.equals(Object)</td><td>3</td><td>3</td><td>2</td><td>4</td></tr><tr><td>Poly.hashCode()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.merge()</td><td>8</td><td>1</td><td>5</td><td>5</td></tr><tr><td>Poly.mulPoly(Poly, Poly)</td><td>29</td><td>1</td><td>10</td><td>10</td></tr><tr><td>Poly.setPolys(HashMap<Special, BigInteger>)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.subPoly(Poly, Poly)</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.toString()</td><td>34</td><td>8</td><td>23</td><td>24</td></tr><tr><td>Poly.tri(Special, String)</td><td>10</td><td>1</td><td>9</td><td>9</td></tr><tr><td>Poly.triesx(int)</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.triesy(int)</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.triesz(int)</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Power.Power(Integer, char)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Power.createPoly()</td><td>3</td><td>1</td><td>4</td><td>4</td></tr><tr><td>Power.toString()</td><td>2</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Special.equals(Object)</td><td>4</td><td>3</td><td>6</td><td>8</td></tr><tr><td>Special.getCos()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getSin()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getX()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getY()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getZ()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.hashCode()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setCos(HashMap<Poly, Integer>)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setSin(HashMap<Poly, Integer>)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setX(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setY(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setZ(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.Term()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.addFactor(Factor)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.createPoly()</td><td>1</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Triangle.Triangle(String, Expr, Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Triangle.createPoly()</td><td>2</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Triangle.toString()</td><td>1</td><td>1</td><td>2</td><td>2</td></tr></tbody></table><p><strong>2.3 HW3</strong></p><p><strong>2.3.1 设计思路</strong></p><ul><li>自定义函数新增定义可以调用已定义的函数,这在架构中十分自然,因为HW2中解析函数等号之后的表达式我传入了一个空的Func类表示没有函数替换的可能性,在这次迭代中只需要将已经定义过的Func类传入Parser中辅助解析即可。</li><li>新增求导算子。如下图我在Expr中新增derivation属性,0代表不微分,1,2,3分别表示对x,y,z求偏微分。同时为每个基本类新增derivation方法,求导在解析完表达式并生成Poly之后调用,与生成Poly的过程类似,是递归调用的过程,尽头是Triangle, Number, Power这三个最底层的类,可直接求导,不需要再往下递归了。</li></ul><p><strong>2.3.2 UML类图</strong> </p><img src="/2023/03/15/2023-03-15-BUAA-OO-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%A7%A3%E6%9E%90/3.png" class title="P3"><p><strong>2.3.3 类复杂度</strong></p><table><thead><tr><th>Class</th><th>OCavg</th><th>OCmax</th><th>WMC</th></tr></thead><tbody><tr><td>Expr</td><td>1.5</td><td>4</td><td>9</td></tr><tr><td>Func</td><td>1.25</td><td>2</td><td>5</td></tr><tr><td>Lexer</td><td>2.75</td><td>7</td><td>11</td></tr><tr><td>MainClass</td><td>2.67</td><td>4</td><td>8</td></tr><tr><td>Number</td><td>1</td><td>1</td><td>4</td></tr><tr><td>Parser</td><td>5.8</td><td>10</td><td>29</td></tr><tr><td>Poly</td><td>4.71</td><td>14</td><td>80</td></tr><tr><td>Power</td><td>2.33</td><td>4</td><td>7</td></tr><tr><td>Special</td><td>1.31</td><td>3</td><td>17</td></tr><tr><td>Term</td><td>1.33</td><td>2</td><td>4</td></tr><tr><td>Triangle</td><td>1.67</td><td>2</td><td>5</td></tr></tbody></table><p>未增加任何类。新增的功能只需在Parser中增加解析求导算子,并且为表达式标记是否需要求导就能够实现。新增求导计算导致Poly的复杂度急剧增加,因为求导法则设计复杂的Poly计算,其方法调用更加频繁。</p><p><strong>2.3.4 方法复杂度</strong></p><table><thead><tr><th>Method</th><th>CogC</th><th>ev(G)</th><th>iv(G)</th><th>v(G)</th></tr></thead><tbody><tr><td>Expr.Expr()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.addTerm(Term, String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.createPoly()</td><td>5</td><td>1</td><td>4</td><td>4</td></tr><tr><td>Expr.getDerivation()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.setDerivation(int)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Expr.toString()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Func.Func(char, ArrayList<Character>, Expr)</Character></td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Func.change(ArrayList<Factor>)</Factor></td><td>1</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Func.getName()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Func.simplify(String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Lexer.Lexer(String)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Lexer.getNumber()</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Lexer.next()</td><td>12</td><td>2</td><td>9</td><td>11</td></tr><tr><td>Lexer.peek()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>MainClass.createFunc(String, ArrayList<Func>)</Func></td><td>6</td><td>1</td><td>4</td><td>4</td></tr><tr><td>MainClass.main(String[])</td><td>1</td><td>1</td><td>2</td><td>2</td></tr><tr><td>MainClass.simplify(String)</td><td>1</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Number.Number(BigInteger)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.addNumber(BigInteger)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.createPoly()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Number.toString()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Parser.Parser(Lexer, ArrayList<Func>)</Func></td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Parser.parseExpr()</td><td>16</td><td>1</td><td>9</td><td>9</td></tr><tr><td>Parser.parseFactor()</td><td>13</td><td>7</td><td>10</td><td>10</td></tr><tr><td>Parser.parseFunc()</td><td>4</td><td>3</td><td>4</td><td>4</td></tr><tr><td>Parser.parseTerm()</td><td>20</td><td>1</td><td>8</td><td>8</td></tr><tr><td>Poly.Poly()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.addPoly(Poly, Poly)</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.cosDerivation(Special, int, HashMap<Special, BigInteger>, HashMap<Special, BigInteger>)</td><td>19</td><td>1</td><td>7</td><td>7</td></tr><tr><td>Poly.derivation(int)</td><td>14</td><td>1</td><td>8</td><td>8</td></tr><tr><td>Poly.equals(Object)</td><td>3</td><td>3</td><td>2</td><td>4</td></tr><tr><td>Poly.getPolys()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.hashCode()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.merge()</td><td>8</td><td>1</td><td>5</td><td>5</td></tr><tr><td>Poly.mulPoly(Poly, Poly)</td><td>29</td><td>1</td><td>10</td><td>10</td></tr><tr><td>Poly.setPolys(HashMap<Special, BigInteger>)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Poly.sinDerivation(Special, int, HashMap<Special, BigInteger>, HashMap<Special, BigInteger>)</td><td>19</td><td>1</td><td>7</td><td>7</td></tr><tr><td>Poly.subPoly(Poly, Poly)</td><td>4</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.toString()</td><td>34</td><td>8</td><td>23</td><td>24</td></tr><tr><td>Poly.tri(Special, String)</td><td>10</td><td>1</td><td>9</td><td>9</td></tr><tr><td>Poly.triesx(int)</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.triesy(int)</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Poly.triesz(int)</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Power.Power(Integer, char)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Power.createPoly()</td><td>3</td><td>1</td><td>4</td><td>4</td></tr><tr><td>Power.toString()</td><td>2</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Special.clone()</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>Special.equals(Object)</td><td>4</td><td>3</td><td>6</td><td>8</td></tr><tr><td>Special.getCos()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getSin()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getX()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getY()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.getZ()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.hashCode()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setCos(HashMap<Poly, Integer>)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setSin(HashMap<Poly, Integer>)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setX(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setY(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Special.setZ(Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.Term()</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.addFactor(Factor)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Term.createPoly()</td><td>1</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Triangle.Triangle(String, Expr, Integer)</td><td>0</td><td>1</td><td>1</td><td>1</td></tr><tr><td>Triangle.createPoly()</td><td>2</td><td>1</td><td>2</td><td>2</td></tr><tr><td>Triangle.toString()</td><td>1</td><td>1</td><td>2</td><td>2</td></tr></tbody></table><p>新增derivation方法,三角函数的derivation尤其复杂,因为我是用Hashmap来储存三角函数的内容(表达式)以及对应的指数的,求导过程中涉及深clone,程序的开销增加。</p><h2 id="三、BUG分析"><a href="#三、BUG分析" class="headerlink" title="三、BUG分析"></a>三、BUG分析</h2><ol><li>第一次作业评测与互测都没有出现bug,十分高兴。在hack他人的过程中将数据进行归类,空白字符夹杂,边界条件如0**0,压力测试等等,可惜均被room里的成员一一化解,room是一个平安夜状态。</li><li>第二次作业强测得分十分低,问题出在由于我第一次作业处理符号的时候是将Term与Term之间的符号存储在上一级Expr的Arraylist中,不给Term单独设置符号。在第二次作业的函数实参因子解析时候,我错误的用的是parseFactor,可能是我解析的是个因子,而因子的英文也是Factor的缘故吧,可惜如果其带负号,就会错误解析。改正方法是用parseExpr,直接把因子视作Expr。(哦悲悲,几个字母的区别)但好消息是互测体会到了狼人的快乐,快乐刀了14刀。同room的成员的bug有:<ul><li>将0**0直接字符串替换为1的,当10**0就会变成11,神奇</li><li>cos(1)**2*cos(1)**2合并为cos(1)**2**2的错误优化</li><li>sin(0)=1的数学错误</li><li>等等,记不太清了</li></ul></li><li>第三次作业强测错了一个点,虽说是一个点,但我很幸运。我在编写代码的时候,在三角函数求完导与未求导部分合并的时候居然不知为何地写成不管未求导部分系数为多少,均视其为1。这可能是个笔误,但中测互测居然没有发现,强测也仅仅错了一个点,有点感觉后背发凉以及无比庆幸。将其修正后就完美的结束了第三次作业。互测中,我只hack了三刀,分别对两位room成员发起:<ul><li>两位成员的bug相同,均为求导算子之后的表达式第一个符号没有储存。</li></ul></li><li>结合三次hack的经历,我认为比较好的策略是:<ul><li>尽量在自己设计的过程中记录可能出现的bug,并构造数据。</li><li>阅读他人代码,寻找薄弱处hack,例如高密度的正则表达式、字符串替换。</li><li>设计数据生成器及自动评测机,提高效率,但是应保证数据的覆盖性。</li></ul></li></ol><h2 id="五、心得体会"><a href="#五、心得体会" class="headerlink" title="五、心得体会"></a>五、心得体会</h2><p>首先感谢本次作业的过程中帮助我的同学和助教,今后面对更大更艰巨的任务与挑战大家还要继续加油。以下是我的体会以及反思。</p><ol><li><p>体会到了递归下降解析的强大,反复递归,使得面向对象的对象概念更加显著。过程式编程很难做到类似解析方式。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Parser</span> {</span><br><span class="line"> <span class="comment">//词法分析器</span></span><br><span class="line"> <span class="keyword">private</span> Lexer lexer;</span><br><span class="line"> <span class="comment">// parseExpr专门解析并返回Expr对象</span></span><br><span class="line"> <span class="keyword">public</span> ...<span class="comment">/*Expr对象*/</span> parseExpr() {</span><br><span class="line"> <span class="comment">// 1、当前 Lexer 读到的是...</span></span><br><span class="line"> <span class="comment">// 2、循环读取 Lexer 的下一个词法</span></span><br><span class="line"> <span class="comment">// 若是...,则调用parseTerm</span></span><br><span class="line"> <span class="comment">// 若不是,则停止循环</span></span><br><span class="line"> <span class="comment">// 3、返回一个Expr对象</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//parseTerm专门解析并返回一个Term对象</span></span><br><span class="line"> <span class="keyword">public</span> ...<span class="comment">/*Term对象*/</span> parseTerm() {</span><br><span class="line"> <span class="comment">// 1、当前 Lexer 读到的是...</span></span><br><span class="line"> <span class="comment">// 2、分类讨论 Lexer 之后读取的内容</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">// 若是...,则调用parseFactor读取Factor对象作为组成部分</span></span><br><span class="line"> <span class="comment">// 3、返回一个Term对象</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// parseFactor专门解析并返回Factor对象</span></span><br><span class="line"> <span class="keyword">public</span> ...<span class="comment">/*数组对象*/</span> parseFactor() {</span><br><span class="line"> <span class="comment">// 1、当前 Lexer 读到的是...</span></span><br><span class="line"> <span class="comment">// 2、循环读取 Lexer 的下一个词法</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">// 3、返回一个Factor对象</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>对我来说最最困难的是HW1的架构搭建,既要考虑以后的可能迭代,又要完成建立合适的类以及类之间的关系来完成本次作业,从过程式编程思想到面向对象编程思想的转变是十分困难的,也不是一下子能成功的,只有在不断地练习中去体会理解它。</p></li><li><p>虽然我完成了三次作业,但是对于性能分这一块,我并没有很好的解决方法去优化三角函数平方和的合并,以及高阶幂的降次。但是在第二次实验中我发现实验课上的<code>simplify</code>方法则十分适合处理该问题,可惜架构已经完成,我不愿意付出时间代价去重构,下次我需*更重视实验与作业结合。</p></li><li><p>数据自动生成与测试知识匮乏,无法搭建合适的评测机,需要向其他同学学习。</p></li></ol><p><strong>总而言之,OO是一门富有挑战性的课,在第一单元中我已经感受到了不知所措与迷茫,但这些挑战能在自己的深思熟虑,不断学习面向对象思想与改进固有思想后逐个解决,解决之后的成就感也是无法言说的。希望我能在未来更大更困难的挑战前也能如此。</strong></p>]]></content>
</entry>
<entry>
<title>第一单元第一次作业</title>
<link href="/2023/03/02/2023-03-01-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E7%AC%AC%E4%B8%80%E6%AC%A1%E4%BD%9C%E4%B8%9A/"/>
<url>/2023/03/02/2023-03-01-%E7%AC%AC%E4%B8%80%E5%8D%95%E5%85%83%E7%AC%AC%E4%B8%80%E6%AC%A1%E4%BD%9C%E4%B8%9A/</url>
<content type="html"><![CDATA[<h1 id="Homework-1"><a href="#Homework-1" class="headerlink" title="Homework 1"></a>Homework 1</h1><p>作业是对表达0式的去括号操作,步骤分为解析表达式和将表达式生成多项式。<br>整体uml类图:</p><h2 id="预处理"><a href="#预处理" class="headerlink" title="预处理"></a>预处理</h2><ul><li>去除空格和’\t’</li><li>连续的加减化成一个,这个操作需要两次</li><li>**替换为^</li></ul><h1 id="递归下降解析表达式"><a href="#递归下降解析表达式" class="headerlink" title="递归下降解析表达式"></a>递归下降解析表达式</h1><h3 id="Lexer"><a href="#Lexer" class="headerlink" title="Lexer"></a>Lexer</h3><p>首先需要知道Number(整数)前面的正负号只会出现在*和^之后,其余情况把正负号作为Expr中连接Term(项)的加减号。所以lexer的解析步骤是:1.遇到数字调用getNumber().2.遇到正负号判断前一个字符是否满足上述要求,满足则作为数字处理,返回一个数字;不满足就只返回正负号。3.其余情况只返回当前pos位置的字符</p><h3 id="Parser"><a href="#Parser" class="headerlink" title="Parser"></a>Parser</h3><h4 id="递归解析表达式(Expr)"><a href="#递归解析表达式(Expr)" class="headerlink" title="递归解析表达式(Expr)"></a>递归解析表达式(Expr)</h4><ul><li><ol><li>读取到[+-]就add到expr储存符号的Arraylist,缺省为+.(符号与Term一一对应,仅代表连接Term的加减号)</li></ol></li><li><ol start="2"><li>解析项.</li></ol></li><li><ol start="3"><li>重复i,区别是如果一开始没有读到[+-],则解析结束.</li></ol></li></ul><h4 id="递归解析项(Term)"><a href="#递归解析项(Term)" class="headerlink" title="递归解析项(Term)"></a>递归解析项(Term)</h4><ul><li><ol><li>解析因子Factor.</li></ol></li><li><ol start="2"><li>读取到的若不为^,则addFactor(),把因子加入Arraylist<Factor>里,若为^则继续读取指数,执行循环将(指数)次加入Arraylist<Factor>.</Factor></Factor></li></ol></li><li><ol start="3"><li>若读取的字符不是’*’则解析Term结束,否则重复i .</li></ol></li></ul><h4 id="递归解析因子(Factor)"><a href="#递归解析因子(Factor)" class="headerlink" title="递归解析因子(Factor)"></a>递归解析因子(Factor)</h4><ul><li><ol><li>读到括号进入表达式解析</li></ol></li><li><ol start="2"><li>读到’xyz’,返回幂函数(Power)</li></ol></li><li><ol start="3"><li>读到数字,返回数字(Number)</li></ol></li></ul><h1 id="表达式生成多项式"><a href="#表达式生成多项式" class="headerlink" title="表达式生成多项式"></a>表达式生成多项式</h1><h3 id="Special"><a href="#Special" class="headerlink" title="Special"></a>Special</h3><p>Special类仅有三个属性,分别存放x,y,z的指数。</p><h3 id="Poly"><a href="#Poly" class="headerlink" title="Poly"></a>Poly</h3><p>Poly属性值有HashMap<Special, BigInteger> polys,建立Special对BigInteger的映射。选择Hashmap的原因是其在多项式加减乘只需要对Hashmap做取并集类似操作,十分方便。</p><h2 id="createPoly-—–在每个类里都有一个createPoly-函数,递归生成表达式"><a href="#createPoly-—–在每个类里都有一个createPoly-函数,递归生成表达式" class="headerlink" title="createPoly()—–在每个类里都有一个createPoly()函数,递归生成表达式"></a>createPoly()—–在每个类里都有一个createPoly()函数,递归生成表达式</h2><h3 id="Number"><a href="#Number" class="headerlink" title="Number"></a>Number</h3><p>(0,0,0)->num</p><h3 id="Power"><a href="#Power" class="headerlink" title="Power"></a>Power</h3><p>(*,0,0)->num or(0,*,0)->num or(0,0,*)->num ,*表示Power里存的x,y,z</p><h3 id="Term"><a href="#Term" class="headerlink" title="Term"></a>Term</h3><p>将Arraylist里的所有Factor生成的表达式相乘,调用mulPoly()</p><h3 id="Expr"><a href="#Expr" class="headerlink" title="Expr"></a>Expr</h3><p>将Arraylist里的所有Term生成的表达式根据相应Arraylist里的符号相加减,调用addPoly()或subPoly()</p><h1 id="整理表达式"><a href="#整理表达式" class="headerlink" title="整理表达式"></a>整理表达式</h1><p>Hashmap合并即可,最后整合一下输出。</p>]]></content>
</entry>
<entry>
<title>汪亭臻是笨蛋的证明</title>
<link href="/2023/01/09/2023-01-09-%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0/"/>
<url>/2023/01/09/2023-01-09-%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0/</url>
<content type="html"><![CDATA[<h1 id="汪亭臻是大笨蛋的证明"><a href="#汪亭臻是大笨蛋的证明" class="headerlink" title="汪亭臻是大笨蛋的证明"></a>汪亭臻是大笨蛋的证明</h1><h2 id="易证,略"><a href="#易证,略" class="headerlink" title="易证,略"></a>易证,略</h2>]]></content>
</entry>
<entry>
<title>tags</title>
<link href="/tags/index.html"/>
<url>/tags/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>link</title>
<link href="/link/index.html"/>
<url>/link/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
</search>