-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
466 lines (225 loc) · 141 KB
/
atom.xml
File metadata and controls
466 lines (225 loc) · 141 KB
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Dys's Blog</title>
<link href="http://example.com/atom.xml" rel="self"/>
<link href="http://example.com/"/>
<updated>2023-11-14T11:58:18.506Z</updated>
<id>http://example.com/</id>
<author>
<name>Dys</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>vim</title>
<link href="http://example.com/2023/11/14/vim/"/>
<id>http://example.com/2023/11/14/vim/</id>
<published>2023-11-14T11:48:32.000Z</published>
<updated>2023-11-14T11:58:18.506Z</updated>
<content type="html"><![CDATA[<p>记录下vim中的常规操作</p><h2 id="替换"><a href="#替换" class="headerlink" title="替换"></a>替换</h2><p>写这个的原因就是某次想在vim中用下全局替换,因为之前都是简单的写、保存、删除、搜索之类的操作,很简单的指令就能完成,但替换这种操作还没有用过,查了下发现还有挺多情况的,所以记录下<br>语法 :[addr]s/source/dst/[option]</p><ul><li>全局替换 %s/str1/str2/g 用str2替换str1</li><li>局部替换 20,30s/str1/str2</li><li>当前行替换 s/str1/str2/g</li><li>手动替换 搜索,cw指令输入替换的字符串,esc退出,n切换到下一个,重复替换.,重复</li></ul><p>addr表示检索范围,省略表示当前行,%表示整个文件,与“1,$”等价,“.,$”表示当前行到文件末尾,s表示替换操作,g表示全局替换,c表示进行确认</p><p>待续~</p>]]></content>
<summary type="html"><p>记录下vim中的常规操作</p>
<h2 id="替换"><a href="#替换" class="headerlink" title="替换"></a>替换</h2><p>写这个的原因就是某次想在vim中用下全局替换,因为之前都是简单的写、保存、删除、搜索之类的操作,很简</summary>
<category term="skill" scheme="http://example.com/tags/skill/"/>
</entry>
<entry>
<title>连不上网了</title>
<link href="http://example.com/2023/11/12/%E8%BF%9E%E4%B8%8D%E4%B8%8A%E7%BD%91%E4%BA%86/"/>
<id>http://example.com/2023/11/12/%E8%BF%9E%E4%B8%8D%E4%B8%8A%E7%BD%91%E4%BA%86/</id>
<published>2023-11-12T15:36:31.000Z</published>
<updated>2023-11-12T15:45:36.816Z</updated>
<content type="html"><![CDATA[<p>如题,我通常的工作环境配置是一部台式机安装ubuntu server 18版本,一台笔记本ssh连接台式机远程完成大部分工作,台式机和笔记本都在校园网下。另外因为平常做的东西经常需要开femu,也就是模拟器,所以需要在ssh连接的这个“远程”环境里再开一个虚拟机。<br>最近想在虚拟机更新东西的时候,发现连不上网了,但是校园网ssh还是能用的,非常神奇,进一步发现,外面的这个机器也连不上网了。虽然更新东西可以通过ssh传文件解决,但是apt更新或者安装一堆乱七八糟的包的时候还是挺麻烦的。经过多方面的排查,包括但不限于重启网卡、修改dns、查看路由等相关配置文件等,但都无果,最后也没看出来是哪里出了问题,很奇怪…<br>最后实在没有办法的办法是改成了校内的镜像源,总算知道了校内镜像源的重要意义了。。。</p>]]></content>
<summary type="html"><p>如题,我通常的工作环境配置是一部台式机安装ubuntu server 18版本,一台笔记本ssh连接台式机远程完成大部分工作,台式机和笔记本都在校园网下。另外因为平常做的东西经常需要开femu,也就是模拟器,所以需要在ssh连接的这个“远程”环境里再开一个虚拟机。<br>最</summary>
<category term="boring" scheme="http://example.com/tags/boring/"/>
</entry>
<entry>
<title>Unknown symbol in module</title>
<link href="http://example.com/2023/10/08/Unknown-symbol-in-module/"/>
<id>http://example.com/2023/10/08/Unknown-symbol-in-module/</id>
<published>2023-10-08T14:47:42.000Z</published>
<updated>2023-10-20T12:54:24.744Z</updated>
<content type="html"><![CDATA[<p>在写模块做测试的时候遇到了很玄学的一个问题:</p><figure class="highlight vbnet"><table><tr><td class="gutter"><pre><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><code class="hljs vbnet">sudo insmod smartdedup.ko<br><span class="hljs-symbol">insmod:</span> <span class="hljs-keyword">ERROR</span>: could <span class="hljs-built_in">not</span> insert <span class="hljs-keyword">module</span> smartdedup.ko: Unknown symbol <span class="hljs-keyword">in</span> <span class="hljs-keyword">module</span><br><span class="hljs-symbol">Makefile:</span><span class="hljs-number">17</span>: recipe <span class="hljs-keyword">for</span> target <span class="hljs-comment">'test' failed</span><br><span class="hljs-symbol">make:</span> *** [test] <span class="hljs-keyword">Error</span> <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>可以看到,在make的时候失败了,因为有什么不知道的符号<br>去网上搜了下,发现具体的错误会在dmesg里提示,看了之后更疑惑了</p><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs accesslog"><span class="hljs-string">[4161060.284598]</span> smartdedup: Unknown symbol rhashtable_init (err <span class="hljs-number">0</span>)<br></code></pre></td></tr></table></figure><p>可以看到明明应该被识别到的很普遍的rhashtable_init函数很神奇的无法识别。、</p><p>首先看下内核的符号表:</p><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gradle">cat <span class="hljs-regexp">/proc/</span>kallsyms | <span class="hljs-keyword">grep</span> rhashtable<br></code></pre></td></tr></table></figure><p>可以看到这个函数确实是在符号表里的(/proc/kallsyms会显示内核中所有的符号,但是这些符号不是都能被其他模块引用的(绝大多数都不能),能被导出的是符号的类型是大写的那些(例如T,U)</p><p>然后我还用了modinfo查看依赖,发现我写的模块确实也是没有依赖的。<br>那么问题到底出现在哪里呢?<br>最后发现是忘了写</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf">MODULE_LICENSE(<span class="hljs-string">"GPL"</span>)<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>应该是因为没有用这个协议,导致这个模块无法使用这个协议约定下暴露出的符号表= =</p><p><strong>许可证</strong> Linux是一款免费的操作系统,采用了GPL协议,允许用户可以任意修改其源代码。GPL协议的主要内容是软件产品中即使使用了某个GPL协议产品提供的库,衍生出一个新产品,该软件产品都必须采用GPL协议,即必须是开源和免费使用的,即GPL协议具有传染性<br>在Linux内核版本2.4.10后,模块必须通过MODULE_LICENSE宏来声明该模块的许可证,否则在加载此模块时,会提示内核被污染。</p><p>部分参考:<br><a href="https://blog.csdn.net/u014436243/article/details/102860177">https://blog.csdn.net/u014436243/article/details/102860177</a><br><a href="https://blog.csdn.net/c4679281314/article/details/118420422">https://blog.csdn.net/c4679281314/article/details/118420422</a></p>]]></content>
<summary type="html"><p>在写模块做测试的时候遇到了很玄学的一个问题:</p>
<figure class="highlight vbnet"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class=</summary>
<category term="kernel" scheme="http://example.com/tags/kernel/"/>
</entry>
<entry>
<title>服务器重启后数据消失</title>
<link href="http://example.com/2023/08/21/%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%87%8D%E5%90%AF%E5%90%8E%E6%95%B0%E6%8D%AE%E6%B6%88%E5%A4%B1/"/>
<id>http://example.com/2023/08/21/%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%87%8D%E5%90%AF%E5%90%8E%E6%95%B0%E6%8D%AE%E6%B6%88%E5%A4%B1/</id>
<published>2023-08-21T13:24:00.000Z</published>
<updated>2023-08-21T13:33:36.202Z</updated>
<content type="html"><![CDATA[<p>一些经常改的项目放在某个自己的服务器的/home/folder2文件夹下,但是重启后突然发现vscode连不上了,最开始还以为是vscode的配置文件的问题,结果连上别的路径开个命令行发现这个目录里没东西了。。属实是吓了一跳<br>后来仔细想想这也不是什么特殊路径不该消失的,于是上网搜了下相关问题,突然发现有人也遇到过相似的问题,原来是硬盘没挂载<br>可以<code>fdisk -l</code>看看所有的分区,然后<code>df -h</code>看看磁盘的情况,可以发现确实有设备没有挂载</p><p>这是因为之前挂载过,但是是临时的挂载,重启之后挂载就失效了,重新用mount指令挂载下,数据就又出现了</p><p>想要一劳永逸地解决这个问题的话,可以修改/etc/fstab文件,让其能够开机自动挂载(但是我懒得改了,反正也不怎么重启电脑= =</p>]]></content>
<summary type="html"><p>一些经常改的项目放在某个自己的服务器的&#x2F;home&#x2F;folder2文件夹下,但是重启后突然发现vscode连不上了,最开始还以为是vscode的配置文件的问题,结果连上别的路径开个命令行发现这个目录里没东西了。。属实是吓了一跳<br>后来仔细想想这也不是什</summary>
<category term="Linux常见问题" scheme="http://example.com/tags/Linux%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/"/>
</entry>
<entry>
<title>kernel模块卸载失败</title>
<link href="http://example.com/2023/08/21/kernel%E6%A8%A1%E5%9D%97%E5%8D%B8%E8%BD%BD%E5%A4%B1%E8%B4%A5/"/>
<id>http://example.com/2023/08/21/kernel%E6%A8%A1%E5%9D%97%E5%8D%B8%E8%BD%BD%E5%A4%B1%E8%B4%A5/</id>
<published>2023-08-20T17:01:45.000Z</published>
<updated>2023-08-20T17:10:48.697Z</updated>
<content type="html"><![CDATA[<p>在平时写内核代码的过程中,用模块进行测试是非常方便的一个方法,但是之前的运行的过程中大部分错误都在编译时就发现了,今天不小心触发了一个运行时的错误,突然发现模块卸载会变得非常困难,于是记录下修复的过程</p><p>场景是这样的,在尝试去rmmod一个已经安装的模块的时候,会一直显示</p><figure class="highlight vbnet"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs vbnet"><span class="hljs-symbol">rmmod:</span> <span class="hljs-keyword">ERROR</span>: <span class="hljs-keyword">Module</span> LRUlist <span class="hljs-built_in">is</span> <span class="hljs-keyword">in</span> use<br></code></pre></td></tr></table></figure><p>但是在尝试去卸载的时候,又会显示</p><figure class="highlight vbnet"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs vbnet"><span class="hljs-symbol">rmmod:</span> <span class="hljs-keyword">ERROR</span>: <span class="hljs-keyword">Module</span> LRUlist <span class="hljs-built_in">is</span> <span class="hljs-keyword">in</span> use<br></code></pre></td></tr></table></figure><p>打开lsmod一看。。。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">Module</span> Size Used by<br><span class="hljs-attribute">LRUlist</span> <span class="hljs-number">16384</span> -<span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>虽然不知道这玩意是啥,但是-1肯定不是好东西对吧<br>后来调了下dmesg,发现原来是因为退出LRUlist的过程中不小心释放了空指针,造成了访问地址错误的问题,于是就无法卸载始终处在装载中的状态了<br>具体的解决方法可以参考这个:<a href="https://blog.csdn.net/gatieme/article/details/75108154">https://blog.csdn.net/gatieme/article/details/75108154</a><br>编译之后按顺序执行</p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs 1c">sudo insmod force_rmmod.ko modname=LRUlist<br>dmesg <span class="hljs-string">| tail -l</span><br>sudo rmmod LRUlist<br>dmesg <span class="hljs-string">| tail -l</span><br>sudo rmmod force_rmmod<br>dmesg <span class="hljs-string">| tail -l</span><br>lsmod<br></code></pre></td></tr></table></figure><p>每执行一步,都看看有没有触发新的问题,有可能的情况是没有成功退出又失败了,我这里当时是因为想要替换exit函数又没有声明那个宏定义导致的。<br>这样就可以继续用模块做测试又不怕失败啦!</p>]]></content>
<summary type="html"><p>在平时写内核代码的过程中,用模块进行测试是非常方便的一个方法,但是之前的运行的过程中大部分错误都在编译时就发现了,今天不小心触发了一个运行时的错误,突然发现模块卸载会变得非常困难,于是记录下修复的过程</p>
<p>场景是这样的,在尝试去rmmod一个已经安装的模块的时候,</summary>
<category term="kernel" scheme="http://example.com/tags/kernel/"/>
</entry>
<entry>
<title>echo重定向permission denied</title>
<link href="http://example.com/2023/08/15/echo%E9%87%8D%E5%AE%9A%E5%90%91permission-denied/"/>
<id>http://example.com/2023/08/15/echo%E9%87%8D%E5%AE%9A%E5%90%91permission-denied/</id>
<published>2023-08-15T14:11:36.000Z</published>
<updated>2023-08-15T14:26:16.061Z</updated>
<content type="html"><![CDATA[<p>有的时候,某些功能需要向一些指定的文件中写入内容,比如:<br>如果有时候kernel显示<code>BUG: soft lockup - CPU#14 stuck for 22s! [init:1]</code>之类的错误,那么一个可行的方法是</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">echo <span class="hljs-number">1</span> > <span class="hljs-regexp">/proc/</span>sys<span class="hljs-regexp">/kernel/</span>softlockup_panic<br></code></pre></td></tr></table></figure><p>这样能够让softlockup触发panic,进而能够显示调用栈,从而找到错误<br>但是直接运行会显示permission denied,即使加上sudo也是这样<br>这是因为重定向功能(也就是“>”)是shell完成的,但是sudo只是给了echo权限,比较容易理解的一种顺序是:<br>shell先调用sudo echo语句,然后echo打印完毕结束之后,superuser shell退出,sudo结束,原始shell将结果收集起来尝试重定向到输出文件,但是权限不足,所以失败(其实详细的流程不是这样,具体过程在文章末给出)<br>所以一种做法就是以root身份登录:</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs css">sudo -<span class="hljs-selector-tag">i</span><br></code></pre></td></tr></table></figure><p>然后就可以直接使用上面提到的指令来让softlockup触发panic了</p><p>事实上,shell可能会先尝试重定向,然后再去尝试前面的指令,如果失败了的话可能连前面的指令都不会去尝试<br>比如一个测试</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><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><code class="hljs stylus">touch ./onlyroot<span class="hljs-selector-class">.txt</span><br>sudo chown root<span class="hljs-selector-pseudo">:root</span> ./onlyroot<span class="hljs-selector-class">.txt</span><br>sudo bash -c <span class="hljs-string">"whoami | tee who.txt"</span> > onlyroot<span class="hljs-selector-class">.txt</span><br>bash: onlyroot<span class="hljs-selector-class">.txt</span>: Permission denied<br></code></pre></td></tr></table></figure><p>这个测试结果中,因为onlyroot.txt文件的权限问题,who.txt文件就根本没有被创建</p><p>本文参考:<br><a href="https://askubuntu.com/questions/230476/how-to-solve-permission-denied-when-using-sudo-with-redirection-in-bash">https://askubuntu.com/questions/230476/how-to-solve-permission-denied-when-using-sudo-with-redirection-in-bash</a><br><a href="https://askubuntu.com/questions/725403/capturing-a-kernel-dump-from-a-cpu-soft-lockup-at-boot">https://askubuntu.com/questions/725403/capturing-a-kernel-dump-from-a-cpu-soft-lockup-at-boot</a></p>]]></content>
<summary type="html"><p>有的时候,某些功能需要向一些指定的文件中写入内容,比如:<br>如果有时候kernel显示<code>BUG: soft lockup - CPU#14 stuck for 22s! [init:1]</code>之类的错误,那么一个可行的方法是</p>
<figure c</summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>kernel 代码踩坑</title>
<link href="http://example.com/2023/08/15/kernel-%E4%BB%A3%E7%A0%81%E8%B8%A9%E5%9D%91/"/>
<id>http://example.com/2023/08/15/kernel-%E4%BB%A3%E7%A0%81%E8%B8%A9%E5%9D%91/</id>
<published>2023-08-15T12:02:56.000Z</published>
<updated>2023-11-14T12:15:48.306Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/kernel%E4%BB%A3%E7%A0%81%E8%B8%A9%E5%9D%91/hollow.jpg" alt="peace & love"><br>刚开始尝试写内核代码,上来就是诡异的bug,因为最开始还没配好debug环境,硬是连蒙带猜搞定的bug。。。</p><h2 id="bug-1"><a href="#bug-1" class="headerlink" title="bug#1"></a>bug#1</h2><p><img src="/../picture/kernel%E4%BB%A3%E7%A0%81%E8%B8%A9%E5%9D%91/memcpy_bug.png" alt="memcmp导致内核崩掉"><br>最开始很疑惑,rhashtable明明是封装好的东西,内部的实现差不多就是根据key算个哈希值分桶,然后再存进去,这个错误里显示的明明就是rhashtable在lookup的过程中因为比较char数组类型的key值而产生了错误。这就让我感到非常疑惑,最开始猜测的方向是:也许内核不支持这么长的char数组?但是发现其他内核文件虽然没有这么长的,但是大于5的uint8数组也是存在的,而memcmp作为比较的默认方法又怎么会出错呢?后来我又想自己写个方法覆盖掉系统原有的memcmp方法,但是中途作罢了,感觉用处不大。最后结合一些信息,我又猜测可能是rhashtable里面存储的元素的问题,内核栈好像是比较小的,而且经常清空,所以如果我添加栈上的元素的话后来可能在比较的时候就会发现访问的是一段不合法的地址,所以改成kzalloc方法分配新的entry就好啦</p><h3 id="内核栈"><a href="#内核栈" class="headerlink" title="内核栈"></a>内核栈</h3><blockquote><p>在用户空间,我们以前所讨论到的那些分配的例子,不少都可以在栈上发生。因为我们毕竟可以事先知道所分配空间的大小。用户空间能够奢侈地负担起非常大的栈,而且栈空间还可以动态增长,相反,内核却不能这么奢侈——内核栈小而固定。当给每个进程分配一个固定大小的小栈后,不但可以减少内存的消耗,而且内核也无需负担太重的栈管理任务。<br>每个进程的内核栈大小既依赖体系结构,也与编译时的选项有关,历史上,每个进程都有两页的内核栈。因为32位和64位体系结构的页面大小分别是4KB和8KB,所以通常他们的内核栈的大小为8KB和16KB。<br>在任意一个函数中,你都必须尽量节省栈资源。这并不难,也没有什么窍门,只需要在具体的函数中让所有的局部变量所占空间之和不要超过几百字节。在栈上进行大量的静态分配(比如分配大型数组或大型结构体)是很危险的下。要不然,在内核中和在用户空间中进行的栈分配就没有什么区别了。栈溢出时悄无声息,但势必会引起严重的问题。因为内核没有在管理内核栈上做足工作,因此,当栈溢出时,多出的数据就会直接溢出来,覆盖掉紧邻栈堆末端的东西。首先面临考验的就是thread_info结构。在堆栈之外,任何内核数据都可能存在潜在的危险。当栈溢出时,最好的情况是机器宕机,最坏的情况是悄无声息的破坏数据。<br>因此,进行动态分配是一种明智的选择,本章前面有关大块内存的分配就是采用这种方式。</p></blockquote><h2 id="bug-2"><a href="#bug-2" class="headerlink" title="bug#2"></a>bug#2</h2><p><img src="/../picture/kernel%E4%BB%A3%E7%A0%81%E8%B8%A9%E5%9D%91/rcu_bug.png" alt="rcu导致的soft lock"><br>这个也很奇怪,我在单独测试rhashtable的时候还没问题,在加到文件系统的时候就出了问题,后来发现有的博主写的代码里不像lwn网站上的那样还写了rcu lock,又查了下rhashtable_look之类的实现,发现里面好像自带了加锁的操作?于是直接把这些乱七八糟的结构都删了,就没有这样的问题了</p><p><a href="https://stackoverflow.com/questions/29848182/how-to-traverse-page-cache-tree-radix-tree-of-a-file-address-space-in-linux-ke">https://stackoverflow.com/questions/29848182/how-to-traverse-page-cache-tree-radix-tree-of-a-file-address-space-in-linux-ke</a></p><h2 id="kernel-oops调试方法"><a href="#kernel-oops调试方法" class="headerlink" title="kernel oops调试方法"></a>kernel oops调试方法</h2><p>比如最简单的NULL pointer oops<br>首先需要在makefile文件里加上调试选项</p><figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs autohotkey">EXTR<span class="hljs-built_in">A_CFLAGS</span> += -g<br></code></pre></td></tr></table></figure><p>这样重新编译出来的结果就是包含debug信息的<br>最关键的就是IP callback_trace 大概知道在哪一行出错,gdb调试还没有试,只是尝试了下objdump反汇编<br>objdump -S xx.ko > temp<br>然后查看temp文件,到对应的行去看出现错误的位置对应的源码是什么</p><p><a href="https://blog.csdn.net/wgl307293845/article/details/109767808">https://blog.csdn.net/wgl307293845/article/details/109767808</a><br><a href="https://kernel.blog.csdn.net/article/details/73715860">https://kernel.blog.csdn.net/article/details/73715860</a></p>]]></content>
<summary type="html"><p><img src="/../picture/kernel%E4%BB%A3%E7%A0%81%E8%B8%A9%E5%9D%91/hollow.jpg" alt="peace &amp; love"><br>刚开始尝试写内核代码,上来就是诡异的bug,因为最开始还没配好de</summary>
<category term="kernel" scheme="http://example.com/tags/kernel/"/>
</entry>
<entry>
<title>学下RCU</title>
<link href="http://example.com/2023/08/08/%E5%AD%A6%E4%B8%8BRCU/"/>
<id>http://example.com/2023/08/08/%E5%AD%A6%E4%B8%8BRCU/</id>
<published>2023-08-08T15:02:05.000Z</published>
<updated>2023-08-08T15:05:44.482Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/rcu/toff.jpg" alt="toff~"><br>学下RCU<br>先放个<a href="https://lwn.net/Articles/262464/">链接</a></p>]]></content>
<summary type="html"><p><img src="/../picture/rcu/toff.jpg" alt="toff~"><br>学下RCU<br>先放个<a href="https://lwn.net/Articles/262464/">链接</a></p>
</summary>
</entry>
<entry>
<title>linux模块,尝试rhashtable</title>
<link href="http://example.com/2023/08/03/linux%E6%A8%A1%E5%9D%97%EF%BC%8C%E5%B0%9D%E8%AF%95rhashtable/"/>
<id>http://example.com/2023/08/03/linux%E6%A8%A1%E5%9D%97%EF%BC%8C%E5%B0%9D%E8%AF%95rhashtable/</id>
<published>2023-08-03T08:01:34.000Z</published>
<updated>2023-08-13T14:08:50.711Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/linux%E6%A8%A1%E5%9D%97/bokita.jpg" alt="mkm"></p><p>为了能够快速构建正确的代码,减少频繁的编译-安装-测试流程的时间,现在尝试先用最简单的模块来测试想要加入的代码的正确性,然后再放到linux源码中一起编译(事实上平常自己做开发也是这个流程,但是写内核的时候感觉人已经晕了,最开始完全没想着这么搞</p><h2 id="先从helloworld模块写起"><a href="#先从helloworld模块写起" class="headerlink" title="先从helloworld模块写起"></a>先从helloworld模块写起</h2><p>先写一段简单的helloworld模块</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs reasonml">#<span class="hljs-keyword">include</span> <linux/init.h><br>#<span class="hljs-keyword">include</span> <linux/<span class="hljs-keyword">module</span>.h><br><br>static <span class="hljs-built_in">int</span> hello<span class="hljs-constructor">_init(<span class="hljs-params">void</span>)</span><br>{<br> printk(KERN_ALERT <span class="hljs-string">"Hello, world\n"</span>);<br> return <span class="hljs-number">0</span>;<br>}<br><br>static void hello<span class="hljs-constructor">_exit(<span class="hljs-params">void</span>)</span><br>{<br> printk(KERN_ALERT <span class="hljs-string">"Goodbye, cruel world\n"</span>);<br>}<br><br><span class="hljs-keyword">module</span><span class="hljs-constructor">_init(<span class="hljs-params">hello_init</span>)</span>;<br><span class="hljs-keyword">module</span><span class="hljs-constructor">_exit(<span class="hljs-params">hello_exit</span>)</span>;<br><br><span class="hljs-constructor">MODULE_AUTHOR(<span class="hljs-string">"dys"</span>)</span>;<br><span class="hljs-constructor">MODULE_DESCRIPTION(<span class="hljs-string">"test"</span>)</span>;<br><span class="hljs-constructor">MODULE_LICENSE(<span class="hljs-string">"GPL"</span>)</span>;<br></code></pre></td></tr></table></figure><p>makefile可以这样写:</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs makefile">KERNELDIR=/lib/modules/<span class="hljs-variable">$(<span class="hljs-built_in">shell</span> uname -r)</span>/build<br>PWD:=<span class="hljs-variable">$(<span class="hljs-built_in">shell</span> pwd)</span><br><span class="hljs-section">obj-m:= helloworld.o</span><br><span class="hljs-section">modules:</span><br><span class="hljs-variable">$(MAKE)</span> -C <span class="hljs-variable">$(KERNELDIR)</span> M=<span class="hljs-variable">$(PWD)</span> modules<br><span class="hljs-section">clean:</span><br>rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions<br><span class="hljs-meta"><span class="hljs-keyword">.PHONY</span>: modules clean</span><br></code></pre></td></tr></table></figure><p>-C 是所需要的内核文件路径,一般这样写就可以<br>M是当前文件夹路径</p><p>这里的$(MAKE) 就相当于make,-C选项的作用是指将当前工作目录转移到你所指定的位置。“M =”选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入“M = dir”,程序会自动到你所指定的dir目录中查找模块源码,将其编译,生成KO文件。 注释:这个是编译driver module的时候必备的makefile 相当于先跳转到 - C 指定的内核目录,然后执行这个目录的makefile M = XXX modules,相当于在当前系统内核根目录下执行make M = XXX modules 进行模块的编译,编译成xx.ko.这样你的modules就可以在任何其他位置,而不用必须在内核文件下面了。</p><p>直接sudo make,Makefile会默认执行第一个伪目标,所以会默认执行sudo make modules</p><p>正常情况下insmod与rmmod不会在屏幕上看到输出,但是输出会到系统日志文件中,如/var/log/syslog,syslog跟其他日志混杂,可以只用kern.log,这里可以这样打印结果</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs stata"><span class="hljs-keyword">cat</span> /<span class="hljs-keyword">var</span>/<span class="hljs-keyword">log</span>/kern.<span class="hljs-keyword">log</span> | tail -2<br></code></pre></td></tr></table></figure><h3 id="尝试rhashtable"><a href="#尝试rhashtable" class="headerlink" title="尝试rhashtable"></a>尝试rhashtable</h3><p>这里有一个lwn的<a href="https://lwn.net/Articles/751374/">链接</a>,rhashtable的简单实现可以照着这个来做。</p><p>这里暂时懒得写了,先贴代码(有空再补</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">#<span class="hljs-keyword">include</span> <linux/init.h><br>#<span class="hljs-keyword">include</span> <linux/<span class="hljs-keyword">module</span>.h><br>#<span class="hljs-keyword">include</span> <linux/rhashtable.h><br><br>#define KEYLENGTH <span class="hljs-number">16</span><br><span class="hljs-keyword">struct</span> <span class="hljs-keyword">object</span><br>{<br> <span class="hljs-built_in">char</span> key<span class="hljs-literal">[KEYLENGTH]</span>;<br> <span class="hljs-keyword">struct</span> rhash_head linkage;<br> <span class="hljs-built_in">char</span> content<span class="hljs-literal">[<span class="hljs-number">64</span>]</span>;<br> <span class="hljs-built_in">int</span> dedup_ref;<br> uint64_t lpa;<br> refcount_t <span class="hljs-built_in">ref</span>;<br> <span class="hljs-keyword">struct</span> rcu_head rcu_head;<br>};<br><br>const static <span class="hljs-keyword">struct</span> rhashtable_params object_params = {<br> .key_len = KEYLENGTH<span class="hljs-operator"> * </span>sizeof(<span class="hljs-built_in">char</span>),<br> .key_offset = offsetof(<span class="hljs-keyword">struct</span> <span class="hljs-keyword">object</span>, key),<br> .head_offset = offsetof(<span class="hljs-keyword">struct</span> <span class="hljs-keyword">object</span>, linkage),<br>};<br><br>static <span class="hljs-built_in">int</span> hello<span class="hljs-constructor">_init(<span class="hljs-params">void</span>)</span><br>{<br> <span class="hljs-keyword">struct</span> rhashtable my_objects;<br> <span class="hljs-built_in">int</span> success;<br> <span class="hljs-built_in">char</span> *key1 = <span class="hljs-string">"aaaaaaaaaaaaaaa"</span>;<br> <span class="hljs-built_in">char</span> *key2 = <span class="hljs-string">"aaa"</span>;<br><br> <span class="hljs-keyword">struct</span> <span class="hljs-keyword">object</span> *found;<br> <span class="hljs-keyword">struct</span> <span class="hljs-keyword">object</span> *found2;<br><br> <span class="hljs-keyword">struct</span> <span class="hljs-keyword">object</span> obj1 = {<br> .key = <span class="hljs-string">"aaaaaaaaaaaaaaa"</span>,<br> .content = <span class="hljs-string">"hello, world"</span>,<br> .dedup_ref = <span class="hljs-number">0</span>,<br> .lpa = <span class="hljs-number">0</span>,<br> };<br> printk(KERN_ALERT <span class="hljs-string">"Hello, world\n"</span>);<br> success = rhashtable<span class="hljs-constructor">_init(&<span class="hljs-params">my_objects</span>, &<span class="hljs-params">object_params</span>)</span>;<br> <span class="hljs-keyword">if</span> (success<span class="hljs-operator"> == </span>-EINVAL)<br> {<br> return -<span class="hljs-number">1</span>;<br> }<br><br> success = rhashtable<span class="hljs-constructor">_lookup_insert_fast(&<span class="hljs-params">my_objects</span>, &<span class="hljs-params">obj1</span>.<span class="hljs-params">linkage</span>, <span class="hljs-params">object_params</span>)</span>;<br> found = rhashtable<span class="hljs-constructor">_lookup_fast(&<span class="hljs-params">my_objects</span>, <span class="hljs-params">key1</span>, <span class="hljs-params">object_params</span>)</span>;<br> found2 = rhashtable<span class="hljs-constructor">_lookup_fast(&<span class="hljs-params">my_objects</span>, <span class="hljs-params">key2</span>, <span class="hljs-params">object_params</span>)</span>;<br> <span class="hljs-keyword">if</span> (found2<span class="hljs-operator"> == </span>NULL)<br> {<br> printk(KERN_ALERT <span class="hljs-string">"not found key 2"</span>);<br> }<br> printk(KERN_ALERT <span class="hljs-string">"%s\n"</span>, found->content);<br> return <span class="hljs-number">0</span>;<br>}<br><br>static void hello<span class="hljs-constructor">_exit(<span class="hljs-params">void</span>)</span><br>{<br> printk(KERN_ALERT <span class="hljs-string">"Goodbye, cruel world\n"</span>);<br>}<br><br><span class="hljs-keyword">module</span><span class="hljs-constructor">_init(<span class="hljs-params">hello_init</span>)</span>;<br><span class="hljs-keyword">module</span><span class="hljs-constructor">_exit(<span class="hljs-params">hello_exit</span>)</span>;<br><br><span class="hljs-constructor">MODULE_AUTHOR(<span class="hljs-string">"dys"</span>)</span>;<br><span class="hljs-constructor">MODULE_DESCRIPTION(<span class="hljs-string">"Flash Friendly File System"</span>)</span>;<br><span class="hljs-constructor">MODULE_LICENSE(<span class="hljs-string">"GPL"</span>)</span>;<br></code></pre></td></tr></table></figure><p><code>success = rhashtable_init(&my_objects, &object_params);</code>init<strong>很重要</strong>!!!如果没写的话可能会编译通过,但是在运行的过程中报dereference null pointer的错误,猜测就是没做init导致使用过程中使用my_objects.xx的时候导致的解引用失败<br>当然,每次改完之后都需要make module-rmmod-insmod-cat,这样一个个输入指令太过于麻烦,可以在makefile文件里一步到位,我新增了test伪目标,然后这样修改:</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs makefile">KERNELDIR=/lib/modules/<span class="hljs-variable">$(<span class="hljs-built_in">shell</span> uname -r)</span>/build<br>PWD:=<span class="hljs-variable">$(<span class="hljs-built_in">shell</span> pwd)</span><br><span class="hljs-section">obj-m:= helloworld.o</span><br><span class="hljs-section">modules:</span><br><span class="hljs-variable">$(MAKE)</span> -C <span class="hljs-variable">$(KERNELDIR)</span> M=<span class="hljs-variable">$(PWD)</span> modules<br><span class="hljs-section">test:</span><br>sudo rmmod helloworld<br>sudo make modules<br>sudo insmod helloworld.ko<br>cat /var/log/kern.log | tail -2<br><span class="hljs-section">clean:</span><br>rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions<br><span class="hljs-meta"><span class="hljs-keyword">.PHONY</span>: modules clean test</span><br></code></pre></td></tr></table></figure><p>这样每次改完代码之后直接make test就好了</p><h2 id="摘抄一点关于pr-xx-内容"><a href="#摘抄一点关于pr-xx-内容" class="headerlink" title="摘抄一点关于pr_xx()内容"></a>摘抄一点关于pr_xx()内容</h2><p>引用自<a href="https://zhuanlan.zhihu.com/p/591630333">链接</a><br>在使用printk的时候需要手动添加输出等级KERN_INFO、KERN_WARNING等,这样还是有些麻烦。因此,Linux内核也对printk进行了进一步的封装。</p><p>Linux内核将每一个输出等级封装为pr_xx()函数,例如,输出等级KERN_INFO封装为pr_info(),输出等级KERN_WARNING封装为pr_warn()。具体如下:</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">#define pr<span class="hljs-constructor">_emerg(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_EMERG pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br><br>#define pr<span class="hljs-constructor">_alert(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_ALERT pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br><br>#define pr<span class="hljs-constructor">_crit(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_CRIT pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br><br>#define pr<span class="hljs-constructor">_err(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_ERR pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br><br>#define pr<span class="hljs-constructor">_warn(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_WARNING pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br><br>#define pr<span class="hljs-constructor">_notice(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_NOTICE pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br><br>#define pr<span class="hljs-constructor">_info(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_INFO pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br><br>#define pr<span class="hljs-constructor">_err(<span class="hljs-params">fmt</span>, <span class="hljs-operator">...</span>)</span> \<br> printk(KERN_ERR pr<span class="hljs-constructor">_fmt(<span class="hljs-params">fmt</span>)</span>, ##__VA_ARGS__)<br></code></pre></td></tr></table></figure><p>这里对输出等级为KERN_DEBUG的封装是比较特殊的,因为debug等级比较常用,内核对pr_debug()分为了三种情况:</p><p>如果设置了 CONFIG_DYNAMIC_DEBUG,则此pr_debug()扩展为 dynamic_pr_debug(),主要用于动态输出。 否则,如果定义了 DEBUG宏,则它等同于具有 KERN_DEBUG 日志级别的 printk。 如果未定义 DEBUG,则它什么都不做。</p><p>以上都是摘抄来的内容,因为printk写起来还是有点麻烦,所以感觉用这个替代下还挺方便的,接下来写代码尝试用用</p><h2 id="试下在内核读文件"><a href="#试下在内核读文件" class="headerlink" title="试下在内核读文件"></a>试下在内核读文件</h2><p>找到的一个方法是在内核调用vfs_read 和 vfs_write,但是在编译的时候会有问题,编译出错,出现 vfs_read[******.ko] undefined !这是因为linux-4.0以后的版本取消了vfs_read()的符号导出EXPORT_SYMBOL(vfs_read)<br>可以使用的方法是kernel_read,调用接口和vfs_read一样,查了下,效果等价于:</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs reasonml">mm_segment_t old_fs;<br>old_fs = get<span class="hljs-constructor">_fs()</span>;<br>set<span class="hljs-constructor">_fs(<span class="hljs-params">get_ds</span>()</span>);<br>vfs<span class="hljs-constructor">_read(<span class="hljs-params">file</span>, (<span class="hljs-params">void</span> <span class="hljs-params">__user</span> <span class="hljs-operator">*</span>)</span>addr, count, &pos);<br>set<span class="hljs-constructor">_fs(<span class="hljs-params">old_fs</span>)</span>;<br></code></pre></td></tr></table></figure><p>这个方法有符号导出,所以可以在编译模块时使用</p>]]></content>
<summary type="html"><p><img src="/../picture/linux%E6%A8%A1%E5%9D%97/bokita.jpg" alt="mkm"></p>
<p>为了能够快速构建正确的代码,减少频繁的编译-安装-测试流程的时间,现在尝试先用最简单的模块来测试想要加入的代码的正确性,然</summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>femu</title>
<link href="http://example.com/2023/07/24/femu/"/>
<id>http://example.com/2023/07/24/femu/</id>
<published>2023-07-24T07:44:50.000Z</published>
<updated>2023-07-24T12:24:38.894Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/femu/bocchi.jpg" alt=":)"></p><p>Femu基于Qemu开发,能够模拟SSD。具体来说,通过模拟读写时延,让虚拟机的用户感觉到虚拟机中好像真的有SSD存在。<br>最近在研究Femu里面的代码,简单记录下。</p><p>blackbox模式下需要在代码里手动调整SSD大小,只在编译的sh脚本里修改是没用的,需要修改<code>ssd_init_params</code>中的这段代码</p><figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs xl"><span class="hljs-function"><span class="hljs-title">spp</span>-></span>secsz = <span class="hljs-number">512</span>;<br><span class="hljs-function"><span class="hljs-title">spp</span>-></span>secs_per_pg = <span class="hljs-number">8</span>;<br><span class="hljs-function"><span class="hljs-title">spp</span>-></span>pgs_per_blk = <span class="hljs-number">256</span>;<br><span class="hljs-function"><span class="hljs-title">spp</span>-></span>blks_per_pl = <span class="hljs-number">256</span>; <span class="hljs-comment">/* 16GB */</span><br><span class="hljs-function"><span class="hljs-title">spp</span>-></span>pls_per_lun = <span class="hljs-number">1</span>;<br><span class="hljs-function"><span class="hljs-title">spp</span>-></span>luns_per_ch = <span class="hljs-number">8</span>;<br><span class="hljs-function"><span class="hljs-title">spp</span>-></span>nchs = <span class="hljs-number">8</span>;<br></code></pre></td></tr></table></figure><p>如果没改的话会有问题,比如说像是在做gc方面的测试的时候,如果这里没有改的话会发现可能触发gc的时机跟自己想的不一样,这是因为这里写死的ssd大小与外面脚本中设定的大小不一致导致的</p><p>代码中的line结构是一种用于gc的数据结构,ssd中没有这个数据结构</p>]]></content>
<summary type="html"><p><img src="/../picture/femu/bocchi.jpg" alt=":)"></p>
<p>Femu基于Qemu开发,能够模拟SSD。具体来说,通过模拟读写时延,让虚拟机的用户感觉到虚拟机中好像真的有SSD存在。<br>最近在研究Femu里面的代码,简单</summary>
<category term="simulator" scheme="http://example.com/tags/simulator/"/>
</entry>
<entry>
<title>koFile</title>
<link href="http://example.com/2023/07/17/koFile/"/>
<id>http://example.com/2023/07/17/koFile/</id>
<published>2023-07-17T13:58:00.000Z</published>
<updated>2023-07-18T14:52:19.265Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/koFile/takagi.jpg" alt=":)"><br>要想将自己写的文件系统代码放到系统中运行,非常常用的方法就是模块化的方法。将文件系统编译成一个单独的模块,然后插入到正在使用的操作系统当中。</p><blockquote><p>尽管Linux是“monolithic”(单内核)的操作系统——这就是说整个系统的内核运行在一个单独的保护域中,但是Linux的内核是模块化组成的,它允许内核在运行时动态地向其中插入或者从中删除代码。这些代码(包括相关的子例程,数据,函数入口和函数出口)被一并组合在一个单独的二进制镜像中,即所谓的可装载内核模块中,简称为模块。支持模块的好处是基本内核镜像可以尽可能的小,因为可选的功能和驱动程序可以利用模块的形式再提供。模块允许我们方便地删除和重新载入内核代码,也方便了调试工作。而且热插拔新设备时,可以通过命令载入新的驱动程序。——《Linux设计与实现》</p></blockquote><p>这个是非常常用的方法,因为直接在现有的机器上改很容易搞崩操作系统,所以通常的做法是改代码->编译成模块->插入虚拟机中的系统进行测试。<br>但是今天我几乎花了一整天的时间试图搞定这一整个流程,还是有点坑的。。</p><p>1.最开始想着直接去femu里面找找,但是发现找不到,后来发现好像发行版的这种系统,比如femu给的ubuntu镜像,是自带一些编译好的模块的,但是去/usr/src下找不到他们的代码,另外还有一种方法是把f2fs的代码文件夹单独搞出来,然后改下makefile,像这个<a href="https://blog.csdn.net/a993096281/article/details/79654208">文章</a>说的,但是因为前面提到,没有找到源码,就算这样改了makefile也不好使,所以最开始想在femu里直接用本身的代码编译出模块的想法失败了</p><p>2.后来想如果找不到自带的源码,那干脆直接下载个源码下来,然后f2fs的make指令指定下载下来的源码。但是这里也踩了点坑。首先需要了解ubuntu版本号都是什么含义:</p><p>这里后面再加</p><p>根据版本号去找对应的版本,然后需要编译源码!编译过生成相应的模块及相关文件再去编译f2fs的时候才能用上这些信息。另外我最开始下载代码的时候忘记是从什么网站下载的了。。。搞了个很奇怪的版本,下载下来的代码编译之后没有ko文件,也可能6.0版本之后的默认设置吧,后来github上找了几个对应的5.x版本的下载,发现编译之后文件夹里直接就出现了ko文件。编译出来之后<code>sudo scp -P 8080 ./f2fs.ko femu@localhost:/home/femu</code>把mod编译出来拷贝过去。但是这里可能还有问题,在insmod的时候报不匹配的问题,这个问题找到了两个博客参考<br><a href="https://unix.stackexchange.com/questions/668868/insmod-coult-not-insert-module-invalid-module-format">https://unix.stackexchange.com/questions/668868/insmod-coult-not-insert-module-invalid-module-format</a><br><a href="https://www.jianshu.com/p/122e6c18e058">https://www.jianshu.com/p/122e6c18e058</a><br>再参考这个<a href="https://zhuanlan.zhihu.com/p/99483997">https://zhuanlan.zhihu.com/p/99483997</a> 把“允许使用为其他内核版本编译的模块”CONFIG_MODVERSIONS给关了,这样编译出来的名字才对(编译一次时间好长,中间有一次就只多写了一个空格名字没对上就不能用。。。)<br><img src="/../picture/koFile/%E5%A4%9A%E7%A9%BA%E6%A0%BC.png" alt="错误大概就长这样,这个图就是多打了一个空格之后insmod失败的结果"></p><p>最后终于成功了。。<br><img src="/../picture/koFile/result.png"><br><a href="https://blog.csdn.net/h2763246823/article/details/122538992">https://blog.csdn.net/h2763246823/article/details/122538992</a><br>这个感觉写的还挺清楚的</p><p>(今天才学到原来git clone -b还可以指定tag号,之前一直以为只能指定branch来着)</p><p>第二天又遇到了个问题,就是如果要用f2fs,那就需要mkfs工具,但是我上面用的femu镜像莫名其妙连不上网,于是各种尝试无果的情况下我直接换了个能用的镜像,然后在里面下载好了mkfs.f2fs,但是发现非常奇怪的问题是像昨天的方法用了之后还是匹配不上,很怪<br><img src="/../picture/koFile/samebutnotuseful.png" alt="这两个一个是能用的,一个是仿照能用的配的名字,很神奇的就是改名字的用不了"></p><p>无奈之下只能再尝试能不能在femu里编译,发现突然又可以了,编译用到的makefile文件如下:</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs makefile"><span class="hljs-comment"># SPDX-License-Identifier: GPL-2.0</span><br>obj-<span class="hljs-variable">$(CONFIG_F2FS_FS)</span> += f2fs.o<br><br>f2fs-y:= dir.o file.o inode.o namei.o hash.o super.o inline.o<br>f2fs-y+= checkpoint.o gc.o data.o node.o segment.o recovery.o<br>f2fs-y+= shrinker.o extent_cache.o sysfs.o<br>f2fs-<span class="hljs-variable">$(CONFIG_F2FS_STAT_FS)</span> += debug.o<br>f2fs-<span class="hljs-variable">$(CONFIG_F2FS_FS_XATTR)</span> += xattr.o<br>f2fs-<span class="hljs-variable">$(CONFIG_F2FS_FS_POSIX_ACL)</span> += acl.o<br>f2fs-<span class="hljs-variable">$(CONFIG_F2FS_IO_TRACE)</span> += trace.o<br>f2fs-<span class="hljs-variable">$(CONFIG_FS_VERITY)</span> += verity.o<br><br>KERNEL_DIR:=/lib/modules/<span class="hljs-variable">$(<span class="hljs-built_in">shell</span> uname -r)</span>/build<br><br>PWD:=/home/folder2/linux-5.4/fs/f2fs<br><br><span class="hljs-section">default:</span><br>make -C <span class="hljs-variable">$(KERNEL_DIR)</span> M=<span class="hljs-variable">$(PWD)</span> modules<br><br><span class="hljs-section">clean:</span><br>rm-rf *.o *.mod.c *.ko Module.symvers modules.order<br></code></pre></td></tr></table></figure><p>感觉昨天用不了的原因可能是KERNEL_DIR不太对?<strong>昨天我虽然用的是/usr/src但是具体到了里面的f2fs文件夹,有可能是这个原因?</strong>有点忘了昨天的错误长啥样了。。。这两天遇到的奇奇怪怪的错误好多。。。<br>然后今天看了下/usr/src确实是软连接到/lib/modules的,这样看跟昨天用的也都是同一个文件夹,感觉很神奇~<br><img src="/../picture/koFile/symlink.png" alt="软连接"></p><p>总结一下就是直接把上面这个makefile放到femu里sudo make一下就能生成相应的模块了,这篇文章其他的内容都是漫长的探索,对真正跑起来项目帮助并不是很大= =</p><p>为什么过了这么久才继续写呢?<br>因为前几个月都在疯狂地调研~ 调研~ 好多论文要看。。。毕竟永远无法看完这个领域的文章,只能大致判断这个方向有没有人做,值不值得做,看完了可能对原来的idea还有点改进的想法,所以看了好久好久。。。其实就像这次写的东西一样,可能做了好久探索了好多奇奇怪怪的内容,最后发现又回到原点了……距离上次写竟然已经有两个月了,时间过得好快~ 看了下之前自己私下写的东西,发现一直在反复思考的点都是motivation~ 继续努力吧!</p>]]></content>
<summary type="html"><p><img src="/../picture/koFile/takagi.jpg" alt=":)"><br>要想将自己写的文件系统代码放到系统中运行,非常常用的方法就是模块化的方法。将文件系统编译成一个单独的模块,然后插入到正在使用的操作系统当中。</p>
<blockqu</summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>qemu</title>
<link href="http://example.com/2023/05/04/qemu/"/>
<id>http://example.com/2023/05/04/qemu/</id>
<published>2023-05-04T13:14:39.000Z</published>
<updated>2023-05-11T14:38:37.938Z</updated>
<content type="html"><![CDATA[<h1 id="qemu与fs"><a href="#qemu与fs" class="headerlink" title="qemu与fs"></a>qemu与fs</h1><p><img src="/../picture/qemu/band.jpg" alt=":)"><br>最近在研究内核,需要虚拟机装载内核然后测试,所以整理下最近研究的关于qemu挂载linux内核的方法以及测试文件系统的方法。<br>主要的参考是<a href="https://blog.csdn.net/louweipu/article/details/121109528">这篇博客</a></p><h2 id="qemu安装"><a href="#qemu安装" class="headerlink" title="qemu安装"></a>qemu安装</h2><p>这里我直接apt安装了</p><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs clean">sudo apt install qemu-<span class="hljs-keyword">system</span><br></code></pre></td></tr></table></figure><p>教程里还教的用<code>sudo apt install qemu</code>,但是这句话我执行失败了,我就不管了,只执行上面那个也能用。<br><img src="/../picture/qemu/pic1.png" alt="有这个应该就行了"></p><h2 id="linux内核下载"><a href="#linux内核下载" class="headerlink" title="linux内核下载"></a>linux内核下载</h2><p>随便找个linux内核代码的网站安装<br>这里我下载的是6.0.1版本,然后</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><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><code class="hljs apache"><span class="hljs-attribute">tar</span> -xvf linux-<span class="hljs-number">6</span>.<span class="hljs-number">0</span>.<span class="hljs-number">1</span>.tar.gz<br><span class="hljs-attribute">cd</span> linux-<span class="hljs-number">6</span>.<span class="hljs-number">0</span>.<span class="hljs-number">1</span><br><span class="hljs-attribute">export</span> ARCH=x86<br><span class="hljs-attribute">make</span> x86_64_defconfig<br></code></pre></td></tr></table></figure><p>然后<code>make menuconfig</code>,需要修改两处设置,在processor type and features里把randomize the address of the kernel image关掉,关闭地址随机化,方便debug<br>(然后开启内核debug,这里我还没找到,很怪<br>(后来找到了<br>然后编译,这里我直接<code>make -j32</code>了<br>记得看下最后生成的image在哪,我的保存在arch/x86/boot/bzImage</p><h2 id="busybox"><a href="#busybox" class="headerlink" title="busybox"></a>busybox</h2><p>暂时还没太看懂这个是干嘛的,先只是照着步骤做了一遍,等看懂了再解释下<br>随便找个版本的busybox下载下,我下载的是1.35.0<br>然后解压<code>tar -jxvf busybox-1.35.0.tar.bz2</code><br>然后进这个目录里<code>make menuconfig</code>,进settings,选择Build static binary(这里也还暂时不太懂干啥的</p><h2 id="制作rootfs"><a href="#制作rootfs" class="headerlink" title="制作rootfs"></a>制作rootfs</h2><p>创建个文件,将其格式化为ext4文件系统</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs routeros">dd <span class="hljs-attribute">if</span>=/dev/zero <span class="hljs-attribute">of</span>=rootfs.img <span class="hljs-attribute">bs</span>=1M <span class="hljs-attribute">count</span>=10<br>mkfs.ext4 rootfs.img<br></code></pre></td></tr></table></figure><p>然后创建目录结构,创建fs然后挂载到rootfs.img</p><figure class="highlight dos"><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><code class="hljs dos"><span class="hljs-built_in">mkdir</span> <span class="hljs-built_in">fs</span><br>sudo mount -t ext4 -o loop rootfs.img ./<span class="hljs-built_in">fs</span><br>sudo make install CONFIG_PREFIX=./<span class="hljs-built_in">fs</span><br></code></pre></td></tr></table></figure><p>然后进fs文件夹里面</p><figure class="highlight tcl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs tcl">sudo mkdir <span class="hljs-keyword">proc</span><span class="hljs-title"> dev</span> etc<span class="hljs-title"> home</span> mnt<span class="hljs-title"></span><br><span class="hljs-title">sudo</span> cp -r<span class="hljs-title"> busybox/examples/bootfloppy/etc/*</span> etc/<br></code></pre></td></tr></table></figure><p>最后<code>sudo chmod -R 777 fs/</code><br>这样就可以了,最后卸载fs就可以了<code>sudo umount fs</code>,这个时候rootfs.img就已经装好busybox了</p><h2 id="启动qemu!!"><a href="#启动qemu!!" class="headerlink" title="启动qemu!!"></a>启动qemu!!</h2><p>我的文件夹结构大概是这样的<br><img src="/../picture/qemu/pic2.png" alt="qemu相关的目录"></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">qemu-system-x86_64 -kernel .<span class="hljs-regexp">/linux-6.0.1/</span>arch<span class="hljs-regexp">/x86/</span>boot<span class="hljs-regexp">/bzImage -hda ./</span>busybox-<span class="hljs-number">1.35</span>.<span class="hljs-number">0</span><span class="hljs-regexp">/rootfs.img -append "root=/</span>dev/sda console=ttyS0<span class="hljs-string">" -nographic -s</span><br></code></pre></td></tr></table></figure><p>进去之后有个问题是没法写,显示readonly,这里还不知道咋改,查了下一个可行的改法是进qemu里面然后<code>mount -o remount rw /</code><br>然后就可以了,更方便的改法还要再研究下</p><blockquote><p>这里我想了想,一种可以的方法貌似是在qemu的语句里声明是可读可写的(但是懒得测了,好像是这样),另一种方法是在etc/init.d/rcS里加上那句<code>mount -o remount rw /</code>,这个貌似是开机启动的脚本,我就用这个顺便改下权限了0.o<br>修改之后我整合进了一个测试脚本,内容如下:</p></blockquote><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs gradle">make -j32<br>cd ~/f2fs_dedup<br><br>qemu-system-x86_64 \<br>-smp <span class="hljs-number">1</span> -m <span class="hljs-number">4</span>G \<br>-kernel .<span class="hljs-regexp">/linux-6.0.1/</span>arch<span class="hljs-regexp">/x86/</span>boot/bzImage \<br>-<span class="hljs-keyword">append</span> <span class="hljs-string">"root=/dev/sda rw console=ttyS0"</span> \<br>-nographic -s -S \<br>-drive <span class="hljs-keyword">file</span>=.<span class="hljs-regexp">/busybox-1_35_0/</span>rootfs.img,index=<span class="hljs-number">0</span>,media=disk,format=raw \<br></code></pre></td></tr></table></figure><p>编译之后直接启动。至于为什么用-drive,是因为老是报一个warning,跟format=raw有关,我就查了下修改成这个样子了</p><h2 id="结合vscode进行gdb调试"><a href="#结合vscode进行gdb调试" class="headerlink" title="结合vscode进行gdb调试"></a>结合vscode进行gdb调试</h2><p>这里主要参考的是<a href="https://blog.csdn.net/weixin_44465434/article/details/121194613">这个文章</a><br>launch.json文件内容如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs bash">{<br> <span class="hljs-string">"version"</span>: <span class="hljs-string">"0.2.0"</span>,<br> <span class="hljs-string">"configurations"</span>: [<br> {<br> <span class="hljs-string">"name"</span>: <span class="hljs-string">"kernel debug"</span>,<br> <span class="hljs-string">"type"</span>: <span class="hljs-string">"cppdbg"</span>,<br> <span class="hljs-string">"request"</span>: <span class="hljs-string">"launch"</span>,<br> <span class="hljs-string">"program"</span>: <span class="hljs-string">"<span class="hljs-variable">${workspaceFolder}</span>/vmlinux"</span>,<br> <span class="hljs-string">"cwd"</span>: <span class="hljs-string">"<span class="hljs-variable">${workspaceFolder}</span>"</span>,<br> <span class="hljs-string">"MIMode"</span>: <span class="hljs-string">"gdb"</span>,<br> <span class="hljs-string">"miDebuggerPath"</span>:<span class="hljs-string">"/usr/bin/gdb-multiarch"</span>,<br> <span class="hljs-string">"miDebuggerServerAddress"</span>: <span class="hljs-string">"127.0.0.1:1234"</span><br> }<br> ]<br>}<br></code></pre></td></tr></table></figure><p>(可能配置可以有的地方不太一样,但是反正这样能跑,中间有一次跑的时候gdb报了个小错,查了下让我改.gdbinit,但是后来发现改了会出问题,就又不碰了)跑出来的结果长这样:<br><img src="/../picture/qemu/debug.png" alt="debug"></p><h2 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h2><h3 id="问题一:qemu挂载内核时不认识f2fs"><a href="#问题一:qemu挂载内核时不认识f2fs" class="headerlink" title="问题一:qemu挂载内核时不认识f2fs"></a>问题一:qemu挂载内核时不认识f2fs</h3><p>要想自己修改文件系统内核并测试,需要在外面先把挂载硬盘格式化为f2fs的形式,大概是mkfs.f2fs,但是这样在挂载的时候会有问题,问题大概是:No filesystem could mount root, tried: ext3 ext2。。。。就是说内核不认识这个硬盘中的数据对应哪种文件系统。这个问题我查了挺久,有的解决方法说是启动脚本的问题,有的说在初始化镜像里应该添加什么什么设置,我尝试了下都没有解决。最后解决的方法是修改了内核的设置,大概是看到<a href="https://stackoverflow.com/questions/17242403/linux-running-self-compiled-kernel-in-qemu-vfs-unable-to-mount-root-fs-on-unk">这个问题以及回答</a>想到的,然后去查了下,在.Config文件里把CONFIG_F2FS_FS设置成y,然后重新编译,需要选的那些东西我都默认选n了,然后就可以识别了!</p><h2 id="待解决问题总结"><a href="#待解决问题总结" class="headerlink" title="待解决问题总结"></a>待解决问题总结</h2><p>现在还有些问题不甚明了,这周要给整明白<br>1.debug问题,以及vscode远程连接图形化debug</p><blockquote><p>解决了</p></blockquote><p>2.只读问题</p><blockquote><p>上面解决了</p></blockquote><p>3.怎么把自己要研究的文件系统内核部件搞到qemu里</p><blockquote><p>先用想要的文件系统格式化挂载硬盘,即在host里mkfs.f2fs挂载的img,然后再放到qemu里,这样启动的时候就会挂载相应的文件系统并用自己设计的内核方式处理文件系统中的数据结构了</p></blockquote><p>4.-S啥意思</p><blockquote><p>进内核的时候等着,方便debug</p></blockquote><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>到这里,对于内核的探索终于迈出了第一步(虽然这一步还没落下来,只能说是半步= =)。接下来就要试着对内核进行修改以及测试了,又将会是踩不完的坑与学不完的知识~</p><p>未完待续~</p>]]></content>
<summary type="html"><h1 id="qemu与fs"><a href="#qemu与fs" class="headerlink" title="qemu与fs"></a>qemu与fs</h1><p><img src="/../picture/qemu/band.jpg" alt=":)"><br></summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>daily</title>
<link href="http://example.com/2023/04/19/daily/"/>
<id>http://example.com/2023/04/19/daily/</id>
<published>2023-04-19T13:10:47.000Z</published>
<updated>2023-04-19T13:14:01.761Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/daily/dream.jpg"></p><p>好菜。。。</p>]]></content>
<summary type="html"><p><img src="/../picture/daily/dream.jpg"></p>
<p>好菜。。。</p>
</summary>
<category term="casual" scheme="http://example.com/tags/casual/"/>
</entry>
<entry>
<title>csapp buflab</title>
<link href="http://example.com/2023/04/12/csapp-buflab/"/>
<id>http://example.com/2023/04/12/csapp-buflab/</id>
<published>2023-04-12T02:56:00.000Z</published>
<updated>2023-04-19T13:10:22.083Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/csappbuflab/5A38812E32B1459B5ABA61625A603E0E.jpg"></p><p>因为一些机缘巧合做了下这个实验,感觉还挺好玩的,记录下~<br>因为从来没有学过CSAPP,上次学计组、看汇编还是两三年前了,所以做的时候感觉还是稍微有点难度的<br>这个实验的目的就是为了了解缓冲区攻击,通过gets函数的缺陷,构造字符串,调整栈帧中溢出的数据,从而实现控制函数行为的目的。<br>该实验共有五个级别:</p><ul><li>Level 0: smoke 让目标程序调用smoke函数</li><li>Level 1: fizz 让目标程序使用特定参数调用Fizz函数</li><li>Level 2: bang 让目标程序调用Bang函数并修改全局变量</li><li>Level 3: boom 无感攻击,并传递有效返回值</li><li>Level 4: kaboom 栈帧地址变化时的有效攻击</li></ul><p>这个实验的难度主要在看汇编代码、理解题意上,如果这两点都能很快完成,那么难度就只有在可能出现bug的时候使用gdb寻找问题<br>实验开始时先用<code>objdump -d bufbomb > output</code>获得可执行文件bufbomb的汇编代码,每个任务都需要查看这个文件中的相应部分代码来完成</p><p><img src="/../picture/csappbuflab/frame.png" alt="栈帧结构示意图(偷的图)"><br>从图中并结合汇编代码可以知道以下信息:</p><ul><li>在函数调用的过程中,栈帧也从高地址向低地址增长</li><li>调用新的函数,也就是执行call指令时,会先将call的下一条指令,即ret地址,压入栈中,然后再将原本的栈帧起始地址压入栈中,完成调用函数部分的保存现场</li><li>在被调用函数中,会将新的esp的地址,也即压入原esp地址后的下一个地址,赋值给ebp。详见各种被调用函数的开头<figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs perl"><span class="hljs-keyword">push</span> %ebp<br>mov %esp,%ebp<br></code></pre></td></tr></table></figure>这时,新的栈帧开始,esp和ebp短暂重合</li><li>通常来说,我们取值的时候给出一个地址,从这个地址开始到大地址取出某几个字节,这个过程中取出的具体的数值可能会因为大小端的差异(本实验用的是小端,所以还要反过来)而不同,但是行为时相同的</li><li>esp寄存器只有push和pop能修改<br>下面实验中,这张图<strong>非常重要!</strong>甚至有可能会反复回来查看,不断地加深了解!</li></ul><h2 id="smoke"><a href="#smoke" class="headerlink" title="smoke"></a>smoke</h2><p>smoke实验需要在getbuf函数ret之后转到smoke函数中执行。<br>这个就非常简单了,只需要让溢出的数据把test调用getbuf函数时压入栈中的返回地址覆盖掉就好了。关于缓冲区buf的起始地址,这里仍然要看汇编代码,具体可能因人而异,但是我的getbuf函数中有这样一句<code>lea -0x3d(%ebp),%eax</code>,这就是说我的buf起始地址在ebp下面0x3d的地方,算一下是61个字节,此时ebp与返回地址之间还有一个原ebp地址,所以需要写入的内容就是<br>61字节(随便什么数据,覆盖buf)+4字节(随便什么数据,test原ebp)+4字节(test返回地址,这里用smoke地址代替)<br>我这里smoke的返回地址是0x08049431,前面再用65个字节的0覆盖,smoke实验就完成了。<br><img src="/../picture/csappbuflab/smoke-answer.png" alt="smoke答案"><br><img src="/../picture/csappbuflab/smoke-result.png" alt="smoke通过测试"></p><h2 id="fizz"><a href="#fizz" class="headerlink" title="fizz"></a>fizz</h2><p>接下来是fizz实验,这个实验要求在getbuf调用结束后跳转到fizz函数而不是return到test函数,另外还要求输入参数。这个实验最难的其实是找到汇编里val在哪里,以及怎样去正确地修改它,所以这个实验需要对栈帧有正确、清晰的认识,可能需要回看上面提到的栈帧示意图,另外对gdb调试也有些要求。<br>先在bufbomb的反汇编文件里找fizz函数,可以看到这样一段指令</p><figure class="highlight llvm"><table><tr><td class="gutter"><pre><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><code class="hljs llvm">mov <span class="hljs-number">0x8</span>(<span class="hljs-variable">%ebp</span>)<span class="hljs-punctuation">,</span><span class="hljs-variable">%edx</span><br>mov <span class="hljs-number">0x804d190</span><span class="hljs-punctuation">,</span><span class="hljs-variable">%eax</span><br>cmp <span class="hljs-variable">%eax</span><span class="hljs-punctuation">,</span><span class="hljs-variable">%edx</span><br>jne <span class="hljs-number">8049492</span> <fizz<span class="hljs-number">+0</span><span class="hljs-keyword">x</span><span class="hljs-number">34</span>><br></code></pre></td></tr></table></figure><p>很明显这里就是val和cookie对比的地方,一个是从栈里取来的,另一个是从某个地址上取来的。很明显,这里从栈上取的就是val,所以在缓冲区攻击时,需要做的任务就包括:</p><ul><li>修改函数返回地址让ret语句能够正确进入fizz函数中</li><li>在栈上相应位置修改val,保证其等于cookie<br>修改地址smoke就已经做过了,这部分可以直接照抄smoke的代码,但这里也有个小问题,就是具体跳到哪里的问题,这个问题结合下面修改val的过程一起思考<br>接下来的问题就是在哪里修改val,这也是这部分内容最难的部分<br>首先可以看到,edx中的值是从<code>0x8(%ebp)</code>中得到的,从栈帧的图来看,是ebp指向的地址再往高地址处偏移八个字节。那么现在的问题就是ebp现在等于什么<br>因为没学过csapp,所以其实我不太清楚汇编中的leave具体是什么意思,查了一下,leave的大概含义是<figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs mel"><span class="hljs-keyword">move</span> %ebp, %esp<br>pop %ebp<br></code></pre></td></tr></table></figure>大概就是把现在的ebp赋值给esp,然后取出来旧的ebp,相当于栈帧前移的操作。ret的作用就是弹出栈顶地址,并将程序转移至那里开始执行<br>所以说,在做这两个操作时,首先程序将esp对准到旧ebp地址所在位置,然后pop两次,一次是旧ebp地址,一次是返回地址。<br>知道了以上信息之后,我们就可以清晰地知道这个实验该怎么做了。我们在实验一写入的时候随便将旧的ebp给覆盖成0了,所以在fizz里找ebp的时候大概率会出问题。(事实上真的出了问题,我最开始写的时候看到fizz开头一堆操作就没细看直接跳到了mov指令处开始比较,结果直接段错误,因为这时候ebp是0,后来一直在gdb调试的时候看寄存器的值是怎么变化的才理解一切~虽然这个ebp相关的内容是下面的实验关心的东西,但是这里其实就可以注意到了).看fizz开头部分,上面提到过,函数开始时,会将原来的ebp压栈,然后将esp赋值给ebp,此时二者对齐。那么汇编直接跳到这里和正常的函数调用有什么区别呢?区别就在于正常的函数调用<strong>会把返回地址压栈!</strong>所以这里与之前比较相当于整体向高地址处偏移了四个字节。我们可以想一下,如果正常的流程来说,如果getbuf返回之后再调用fizz,那么这二者的栈帧按理来说是一样的,但是就是因为没有显式地调用,所以没有压栈返回地址,所以这时候旧ebp保存的地方就是getbuf调用的时候保存返回地址的地方。最后写结果的时候,首先缓冲区溢出,在返回地址写上目标地址,这个地址在进入fizz函数之后成为了ebp指向的位置。在这个地址+8字节的地方保存了cookie,所以写四个字节的0,然后写入cookie<br><img src="/../picture/csappbuflab/fizz-answer.png"><br><img src="/../picture/csappbuflab/fizz-result.png"><br>其实我在这里做的时候用了很久gdb,因为最开始做的时候没太整明白leave和ret的作用,所以基本是靠gdb理解的,然后第一次写还遇到了点小问题,也是用gdb看了下汇编单步调试整明白的</li></ul><h2 id="bang"><a href="#bang" class="headerlink" title="bang"></a>bang</h2><p>这个实验大概要求就是通过写入buf的形式把攻击程序也写入到栈上,这样在getbuf的最后ret的时候将返回地址控制到攻击程序的起点处,待攻击程序执行完毕后再跳到bang函数中。<br>其他操作前面都用到过,主要是两个问题:攻击代码内容是什么和攻击代码放在哪里。<br>攻击代码里应该将global_value啊设置为cookie值,然后将bang的地址压入栈中,再用ret语句返回到bang。global_value的值可以通过反汇编出的汇编代码查看</p><figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs x86asm"><span class="hljs-keyword">mov</span> <span class="hljs-number">0x804d198</span>,%eax<br><span class="hljs-keyword">mov</span> %eax,%edx<br><span class="hljs-keyword">mov</span> <span class="hljs-number">0x804d190</span>,%eax<br><span class="hljs-keyword">cmp</span> %eax,%edx<br><span class="hljs-keyword">jne</span> 80494ea <bang+<span class="hljs-number">0x3b</span>><br><span class="hljs-keyword">mov</span> <span class="hljs-number">0x804d198</span>,%eax<br></code></pre></td></tr></table></figure><p>只看前面好像还不太好判断,但是看条件跳转正确的情况下是需要print global_value的,汇编后续的操作里将0x804d198压到栈里了,所以很明显这里就是保存global_value的地方。所以攻击代码就很明了了</p><figure class="highlight apache"><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><code class="hljs apache"><span class="hljs-attribute">movl</span> $<span class="hljs-number">0</span>x57dcd3f7,<span class="hljs-number">0</span>x804d198<br><span class="hljs-attribute">push</span> $<span class="hljs-number">0</span>x080494af<br><span class="hljs-attribute">ret</span><br></code></pre></td></tr></table></figure><p>然后汇编反汇编得到汇编代码</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs mipsasm">gcc -m32 -c <span class="hljs-keyword">bang.S</span><br><span class="hljs-keyword"></span>objdump -d <span class="hljs-keyword">bang.o </span>> <span class="hljs-keyword">bang_output</span><br></code></pre></td></tr></table></figure><p>大概长这样</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs apache"><span class="hljs-attribute">00000000</span> <.text>:<br> <span class="hljs-attribute">0</span>:c7 <span class="hljs-number">05</span> <span class="hljs-number">98</span> d1 <span class="hljs-number">04</span> <span class="hljs-number">08</span> f7 movl $<span class="hljs-number">0</span>x57dcd3f7,<span class="hljs-number">0</span>x804d198<br> <span class="hljs-attribute">7</span>:d3 dc <span class="hljs-number">57</span> <br> <span class="hljs-attribute">a</span>:<span class="hljs-number">68</span> af <span class="hljs-number">94</span> <span class="hljs-number">04</span> <span class="hljs-number">08</span> push $<span class="hljs-number">0</span>x80494af<br> <span class="hljs-attribute">f</span>:c3 ret <br></code></pre></td></tr></table></figure><p>接下来就是攻击代码放在哪里的问题,按理来说放在栈上应该都可以,为了方便起见,我放到了原来的getbuf栈帧的位置,写入的数据少一点<br><img src="/../picture/csappbuflab/bang-answer.png"><br>最后一行是算出来的程序首地址,这个文件里写的内容在栈里看是从低地址到高地址的,所以应该跳到比较远的地方,然后顺序取址</p><h2 id="boom"><a href="#boom" class="headerlink" title="boom"></a>boom</h2><p>其实就跟上面上面bang的过程一模一样,我懒得写了。。<br>大概就是返回值cookie保存到eax里,然后把test里调用getbuf的下一句的地址压栈,然后ret<br>但是这个实验比较特殊的是需要返回到test里且程序没有感知,栈状态需要完全一致,这里我最开始没太懂是什么意思,运行出错之后gdb调试才发现是ebp没设置好,那就看下原来的是什么然后写入数据的时候稍微注意下喽~<br><img src="/../picture/csappbuflab/boom-answer.png"></p><h2 id="kaboom"><a href="#kaboom" class="headerlink" title="kaboom"></a>kaboom</h2><p>这个相当于加了个比较真实的场景,因为之前的几个实验里那几个栈帧的地址都是不动的,不太符合实际。这个实验多次运行,每次运行的时候栈帧的起始地址都是个随机的值。<br>这个实验里解决的方法就是猜一个大概会跳到的地址,然后从那个地址开始用nop指令填充,再在确定的地址上放上需要执行的程序,所以区别就只有添加大量的nop指令而已。<br>懒得写了~</p><p>未完待续~</p>]]></content>
<summary type="html"><p><img src="/../picture/csappbuflab/5A38812E32B1459B5ABA61625A603E0E.jpg"></p>
<p>因为一些机缘巧合做了下这个实验,感觉还挺好玩的,记录下~<br>因为从来没有学过CSAPP,上次学计组、看汇编还是</summary>
<category term="CSAPP" scheme="http://example.com/tags/CSAPP/"/>
</entry>
<entry>
<title>6-s081-lab8-Lock</title>
<link href="http://example.com/2023/03/31/6-s081-lab8-Lock/"/>
<id>http://example.com/2023/03/31/6-s081-lab8-Lock/</id>
<published>2023-03-31T06:38:57.000Z</published>
<updated>2023-04-01T11:23:29.578Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/6.s081lab8lock/star.jpg" alt="真好啊~"></p><h1 id="Lock"><a href="#Lock" class="headerlink" title="Lock"></a>Lock</h1><p>这个实验给我的感觉就是在尝试与锁去妥协,很多场景为了并发的考虑必须去用锁,但是很多自旋锁因为一直在while循环耗时太长,应该就是锁争用导致的阻塞,所以需要许多手段去尽可能减少锁的争用。</p><h2 id="Memory-allocator"><a href="#Memory-allocator" class="headerlink" title="Memory allocator"></a>Memory allocator</h2><p>lab1 是针对memory allocator的优化。<br>在kalloctest中之所以会出现争用,最根本的问题就是因为只有一个free list,所有的加锁申请都要申请这个链表上的锁,所以争用很大。这个lab要实现的优化就是每个CPU维护一个free list,每个free list用一个锁,这样在不同CPU上的allocation和free就可以并行。这样有可能会产生的一个问题是一个free list空了,但是另一个CPU free list有空闲的空间,这时候就需要去窃取别的CPU的free list。</p><ol><li>首先修改kmem的定义,把这个修改成NCPU大小的数组<figure class="highlight gauss"><table><tr><td class="gutter"><pre><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><code class="hljs gauss"><span class="hljs-keyword">struct</span> {<br> <span class="hljs-keyword">struct</span> <span class="hljs-type">spinlock</span> lock;<br> <span class="hljs-keyword">struct</span> <span class="hljs-type">run</span> *freelist;<br>} kmem[NCPU];<br></code></pre></td></tr></table></figure></li><li>接下来就需要配套地去修改所有用到freelist的地方。首先是kinit()。这里需要给每个锁名字,按照提示都给kmem这个名字就可以。这里freerange的意思是把这段地址上所有的页都放到freelist里,最开始我想的是既然这个实验是需要多CPU上的freelist,那么这个可能也是需要把freerange放到for循环里。但是这个想法显然是错误的,这个freerange函数只需要调用一次就可以了,先把页都放在一个freelist上,然后需要的时候再去窃取<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">void</span></span><br><span class="hljs-function"><span class="hljs-title">kinit</span>()</span><br>{<br> <span class="hljs-keyword">for</span>(<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>;i<NCPU;i++){<br> initlock(&kmem[i].<span class="hljs-keyword">lock</span>, <span class="hljs-string">"kmem"</span>);<br> }<br> <span class="hljs-comment">//这里最开始要放在外面,全部的free都给一个cpu</span><br> <span class="hljs-keyword">for</span>(<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>;i<NCPU;i++){<br> kmem[i].freelist = <span class="hljs-number">0</span>;<br> }<br> freerange(end, (<span class="hljs-keyword">void</span>*)PHYSTOP); <br>}<br></code></pre></td></tr></table></figure></li><li>kfree()也很简单,用cpuid()获取一下当前cpu,然后仿照原来的代码把当前页放到链表上就可以了。这里感觉有一个很有意思的操作。首先把地址上PGSIZE大小的内容都赋值为1,重置其内容。然后强转这个地址,转成run,这句话的意思就是在这个页开头run大小的位置上放上一个struct run,然后加锁修改这个run的内容。这也对应了书中提到的,空闲链表的结构是维护在空闲memory page里的,需要用的时候也会直接覆盖掉。另外还要注意下push 和 pop的问题。<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs fsharp"><span class="hljs-keyword">void</span><br>kfree(<span class="hljs-keyword">void</span> <span class="hljs-operator">*</span>pa)<br>{<br> <span class="hljs-comment">//这里push pop最开始忘了</span><br> push_off();<br> <span class="hljs-keyword">struct</span> run <span class="hljs-operator">*</span>r;<br><br> <span class="hljs-keyword">if</span>(((uint64)pa <span class="hljs-operator">%</span> PGSIZE) <span class="hljs-operator">!=</span> <span class="hljs-number">0</span> <span class="hljs-operator">||</span> (char<span class="hljs-operator">*</span>)pa <span class="hljs-operator"><</span> <span class="hljs-keyword">end</span> <span class="hljs-operator">||</span> (uint64)pa <span class="hljs-operator">>=</span> PHYSTOP)<br> panic(<span class="hljs-string">"kfree"</span>);<br> <span class="hljs-comment">// Fill with junk to catch dangling refs.</span><br> memset(pa, <span class="hljs-number">1</span>, PGSIZE);<br><br> r <span class="hljs-operator">=</span> (<span class="hljs-keyword">struct</span> run<span class="hljs-operator">*</span>)pa;<br> int <span class="hljs-built_in">id</span> <span class="hljs-operator">=</span> cpuid();<br> acquire(<span class="hljs-operator">&</span>kmem[<span class="hljs-built_in">id</span>].<span class="hljs-built_in">lock</span>);<br> r<span class="hljs-operator">-></span>next <span class="hljs-operator">=</span> kmem[<span class="hljs-built_in">id</span>].freelist;<br> kmem[<span class="hljs-built_in">id</span>].freelist <span class="hljs-operator">=</span> r;<br> release(<span class="hljs-operator">&</span>kmem[<span class="hljs-built_in">id</span>].<span class="hljs-built_in">lock</span>);<br> pop_off();<br>}<br></code></pre></td></tr></table></figure></li><li>kalloc也仿照原本的内容,需要加的东西就是如果没有空闲的freelist了,那么就去别的cpu上窃取。我的做法是从当前的cpuid开始加一,直到加到自己的大小为止。在这个过程中,如果遇到能用的freelist就用一下,另外还要稍微注意下加锁释放锁的问题。<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs fsharp"><span class="hljs-keyword">void</span> <span class="hljs-operator">*</span><br>kalloc(<span class="hljs-keyword">void</span>)<br>{<br> push_off();<br> <span class="hljs-keyword">struct</span> run <span class="hljs-operator">*</span>r;<br> int <span class="hljs-built_in">id</span> <span class="hljs-operator">=</span> cpuid();<br> acquire(<span class="hljs-operator">&</span>kmem[<span class="hljs-built_in">id</span>].<span class="hljs-built_in">lock</span>);<br> r <span class="hljs-operator">=</span> kmem[<span class="hljs-built_in">id</span>].freelist;<br> <span class="hljs-keyword">if</span>(r){<br> kmem[<span class="hljs-built_in">id</span>].freelist <span class="hljs-operator">=</span> r<span class="hljs-operator">-></span>next;<br> release(<span class="hljs-operator">&</span>kmem[<span class="hljs-built_in">id</span>].<span class="hljs-built_in">lock</span>);<br> }<span class="hljs-keyword">else</span>{<br> release(<span class="hljs-operator">&</span>kmem[<span class="hljs-built_in">id</span>].<span class="hljs-built_in">lock</span>);<br> <span class="hljs-keyword">for</span>(int i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;i<span class="hljs-operator"><</span>NCPU;i<span class="hljs-operator">++</span>){<br> int new_id <span class="hljs-operator">=</span> (<span class="hljs-built_in">id</span><span class="hljs-operator">+</span>i)<span class="hljs-operator">%</span>NCPU;<br> acquire(<span class="hljs-operator">&</span>kmem[new_id].<span class="hljs-built_in">lock</span>);<br> <span class="hljs-keyword">if</span>(kmem[new_id].freelist){<br> r <span class="hljs-operator">=</span> kmem[new_id].freelist;<br> kmem[new_id].freelist <span class="hljs-operator">=</span> r<span class="hljs-operator">-></span>next;<br> <span class="hljs-comment">//这里最开始竟然忘了break了,很离谱的错误。。。写着写着忘掉了</span><br> release(<span class="hljs-operator">&</span>kmem[new_id].<span class="hljs-built_in">lock</span>);<br> break;<br> }<br> release(<span class="hljs-operator">&</span>kmem[new_id].<span class="hljs-built_in">lock</span>);<br> }<br> }<br><br> <span class="hljs-keyword">if</span>(r)<br> memset((char<span class="hljs-operator">*</span>)r, <span class="hljs-number">5</span>, PGSIZE); <span class="hljs-comment">// fill with junk</span><br> pop_off();<br> <span class="hljs-keyword">return</span> (<span class="hljs-keyword">void</span><span class="hljs-operator">*</span>)r;<br>}<br></code></pre></td></tr></table></figure>这里最开始忘了break按理来说好像应该是会报错,有点忘了,隔的时间有点长了</li></ol><h2 id="Buffer-cache"><a href="#Buffer-cache" class="headerlink" title="Buffer cache"></a>Buffer cache</h2><p>第二部分涉及到文件系统buffer cache层的东西,但是跟文件系统关系不大,跟lab1的目标差不太多,都是为了减少锁争用修改加锁策略的东西。<br>这部分是文件系统内部的东西,bcache可能会有多个线程来争用。这里的思路其实是相似的,也是把争用的对象分散开来的想法。但是不太相同的地方是:kalloc中可以给每个cpu一个freelist,但是因为kalloc在进程之间共享,所以可以想办法用哈希的方式把申请bcache的需求分散开来<br>这里主要讲一下实现思路。这里我是直接把bcache定义成了BUCKET_NUM大小的桶,每个桶对应一串哈希值,这一串哈希值在数组中存储,BUCKET_NUM建议设置成13,因为别的容易冲突。</p><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs gauss"><span class="hljs-keyword">struct</span> {<br> <span class="hljs-keyword">struct</span> <span class="hljs-type">spinlock</span> lock;<br> <span class="hljs-keyword">struct</span> <span class="hljs-type">buf</span> hash_bucket[NBUF/BUCKET_NUM + <span class="hljs-number">1</span>];<br> <span class="hljs-comment">//struct buf head;</span><br>} bcache[BUCKET_NUM];<br></code></pre></td></tr></table></figure><p>然后就是在binit里初始化,因为涉及到时间的初始化,需要注意的一点是需要先得到时间的值,然后for循环去赋值,不然会因为for循环的时间导致初始化的时间不一样。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">void</span></span><br><span class="hljs-function"><span class="hljs-title">binit</span>(<span class="hljs-params"><span class="hljs-keyword">void</span></span>)</span><br>{<br> <span class="hljs-comment">//struct buf *b;</span><br> <span class="hljs-built_in">uint</span> init_ticks = ticks;<br> <span class="hljs-keyword">for</span>(<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>;i<BUCKET_NUM;i++){<br> initlock(&bcache[i].<span class="hljs-keyword">lock</span>, <span class="hljs-string">"bcache"</span>);<br> }<br> <span class="hljs-keyword">for</span>(<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i<BUCKET_NUM;i++){<br> <span class="hljs-keyword">for</span>(<span class="hljs-built_in">int</span> j = <span class="hljs-number">0</span>;j<NBUF/BUCKET_NUM+<span class="hljs-number">1</span>;j++){<br> <span class="hljs-comment">//这里每次循环都赋值的话每次时间都不一样,得最开始取个时间然后遍历赋值</span><br> bcache[i].hash_bucket[j].ticks = init_ticks;<br> <span class="hljs-comment">//initsleeplock(&bcache[i].hash_bucket[j].lock, "buffer");</span><br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>bget()需要完成的功能就是分配buffer,如果能找到blockno的cache,那么就返回,不然就重新alloc一个然后赋值。这里找的过程只需要注意一下加锁和释放锁就行,基本照着原来的照抄就可以。<br>如果没找到的话就找一个最久远的然后引用量还是0的替换出去,这里基本也是照抄就可以</p><figure class="highlight inform7"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><code class="hljs inform7">static struct buf*<br>bget(uint dev, uint blockno)<br>{<br> uint original_hash = (dev%BUCKET_NUM + blockno%BUCKET_NUM)%BUCKET_NUM;<br><br> acquire(&bcache<span class="hljs-comment">[original_hash]</span>.lock);<br> struct buf *b;<br> b = bcache<span class="hljs-comment">[original_hash]</span>.hash_bucket;<br> for(int i = 0;i<NBUF/BUCKET_NUM+1;i++){<br> if(b<span class="hljs-comment">[i]</span>.dev==dev && b<span class="hljs-comment">[i]</span>.blockno == blockno){<br> b<span class="hljs-comment">[i]</span>.refcnt+=1;<br> release(&bcache<span class="hljs-comment">[original_hash]</span>.lock);<br> acquiresleep(&b<span class="hljs-comment">[i]</span>.lock);<br> //b<span class="hljs-comment">[i]</span>.ticks = ticks;<br> return &b<span class="hljs-comment">[i]</span>;<br> }<br> }<br> /*<br> for(int i = 0; i < NBUF/BUCKET_NUM + 1; i++){<br> if(bcache.hash_bucket<span class="hljs-comment">[original_hash]</span><span class="hljs-comment">[i]</span>.dev==dev && bcache.hash_bucket<span class="hljs-comment">[original_hash]</span><span class="hljs-comment">[i]</span>.blockno == blockno){<br> bcache.hash_bucket<span class="hljs-comment">[original_hash]</span><span class="hljs-comment">[i]</span>.refcnt+=1;<br> release(&bcache.lock<span class="hljs-comment">[original_hash]</span>);<br> acquiresleep(&bcache.hash_bucket<span class="hljs-comment">[original_hash]</span><span class="hljs-comment">[i]</span>.lock);<br> return bcache.hash_bucket<span class="hljs-comment">[original_hash]</span><span class="hljs-comment">[i]</span>;<br> }<br> }<br> */<br> // <span class="hljs-keyword">Is</span> the block already cached?<br> /*<br> for(b = bcache.head.next; b != &bcache.head; b = b->next){<br> if(b->dev == dev && b->blockno == blockno){<br> b->refcnt++;<br> release(&bcache.lock);<br> acquiresleep(&b->lock);<br> return b;<br> }<br> }<br> */<br> // Not cached.<br> // Recycle the least recently used (LRU) unused buffer.<br> uint min = ~0;<br> int index = -1;<br> for(int i = 0;i < NBUF/BUCKET_NUM + 1; i++){<br> if(b<span class="hljs-comment">[i]</span>.ticks <= min && b<span class="hljs-comment">[i]</span>.refcnt == 0){<br> min = b<span class="hljs-comment">[i]</span>.ticks;<br> index = i;<br> }<br> }<br> if(b<span class="hljs-comment">[index]</span>.refcnt == 0){<br> b<span class="hljs-comment">[index]</span>.dev = dev;<br> b<span class="hljs-comment">[index]</span>.blockno = blockno;<br> b<span class="hljs-comment">[index]</span>.valid = 0;<br> b<span class="hljs-comment">[index]</span>.refcnt = 1;<br> //b<span class="hljs-comment">[index]</span>.ticks = ticks;<br> release(&bcache<span class="hljs-comment">[original_hash]</span>.lock);<br> acquiresleep(&b<span class="hljs-comment">[index]</span>.lock);<br> //b<span class="hljs-comment">[index]</span>.ticks = ticks;<br> return &b<span class="hljs-comment">[index]</span>;<br> }<br> /*<br> for(b = bcache.head.prev; b != &bcache.head; b = b->prev){<br> if(b->refcnt == 0) {<br> b->dev = dev;<br> b->blockno = blockno;<br> b->valid = 0;<br> b->refcnt = 1;<br> release(&bcache.lock);<br> acquiresleep(&b->lock);<br> return b;<br> }<br> }<br> */<br> panic(<span class="hljs-string">"bget: no buffers"</span>);<br>}<br></code></pre></td></tr></table></figure><p>然后就是小修小补了,把那些bache.lock都改成相应的bcache[original_hash].lock就可以了<br>其实做到这里实验一直无法通过,感觉这里虽然让用哈希桶,但是可能还是需要去resize一下的,虽然提示里说不需要,但是不resize的话可能导致几个进程一起争用一个(不过我感觉我这个可能是死锁的问题没解决,不然应该不会卡住,只能说有时间再看下吧~)这里最后用了个比较取巧的方法,我修改了NBUF的定义</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> NBUF (MAXOPBLOCKS*3+30)</span><br></code></pre></td></tr></table></figure><p>这样就顺利地混过了实验~感觉这个实验还是挺有意义的,就是我太菜了,没法全部体会到。</p><p><strong>注一:</strong>这个实验需要把param.h里的FSSIZE手动调大一点,我改到了10000,不然的话会报panic: balloc: out of blocks的错误,这个我是查到了别人也遇到这个问题的博客才解决<a href="https://www.cnblogs.com/duile/p/16389164.html">https://www.cnblogs.com/duile/p/16389164.html</a></p>]]></content>
<summary type="html"><p><img src="/../picture/6.s081lab8lock/star.jpg" alt="真好啊~"></p>
<h1 id="Lock"><a href="#Lock" class="headerlink" title="Lock"></a>Lock</h1</summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>6-s081-lab6-Multithreading</title>
<link href="http://example.com/2023/03/31/6-s081-lab6-Multithreading/"/>
<id>http://example.com/2023/03/31/6-s081-lab6-Multithreading/</id>
<published>2023-03-31T06:38:41.000Z</published>
<updated>2023-04-04T09:11:56.025Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/6.s081lab6multi/star.jpg" alt="换换口味,来个白金之星("></p><h1 id="Multithreading"><a href="#Multithreading" class="headerlink" title="Multithreading"></a>Multithreading</h1><h2 id="Uthread"><a href="#Uthread" class="headerlink" title="Uthread"></a>Uthread</h2><p>这个实验比较简单,看完xv6 book相应章节之后对内核中thread的切换有了一定的了解之后就可以完成。<br>首先为了能够保存线程的状态,最重要的就是能够保存当前的上下文,用于后续切换回这个线程的时候恢复现场。</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs abnf">struct context {<br> uint64 ra<span class="hljs-comment">;</span><br> uint64 sp<span class="hljs-comment">;</span><br><br> // callee-saved<br> uint64 s0<span class="hljs-comment">;</span><br> uint64 s1<span class="hljs-comment">;</span><br> uint64 s2<span class="hljs-comment">;</span><br> uint64 s3<span class="hljs-comment">;</span><br> uint64 s4<span class="hljs-comment">;</span><br> uint64 s5<span class="hljs-comment">;</span><br> uint64 s6<span class="hljs-comment">;</span><br> uint64 s7<span class="hljs-comment">;</span><br> uint64 s8<span class="hljs-comment">;</span><br> uint64 s9<span class="hljs-comment">;</span><br> uint64 s10<span class="hljs-comment">;</span><br> uint64 s11<span class="hljs-comment">;</span><br>}<span class="hljs-comment">;</span><br><br><br>struct thread {<br> char stack[STACK_SIZE]<span class="hljs-comment">; /* the thread's stack */</span><br> int state<span class="hljs-comment">; /* FREE, RUNNING, RUNNABLE */</span><br> struct context context<span class="hljs-comment">;</span><br>}<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>这里照抄内核的代码就行<br>uthread_switch.S也是照抄就可以<br>接下来的任务就是稍微修改下thread_switch和thread_create<br>在thread_create里需要加上</p><figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs xl"><span class="hljs-function"><span class="hljs-title">t</span>-></span>context.ra = (uint64)func;<br><span class="hljs-function"><span class="hljs-title">t</span>-></span><span class="hljs-function"><span class="hljs-title">context</span>.sp = (uint64)t-></span>stack + STACK_SIZE;<br></code></pre></td></tr></table></figure><p>因为这两个一个是返回地址,一个是线程栈的地址,都需要在初始化的时候赋值,context的其他寄存器都是在运行的过程中保存临时结果使用的。<br>最后在switch里面加一句</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs lisp">thread_switch((<span class="hljs-name">uint64</span>)(<span class="hljs-name">&t->context</span>),(<span class="hljs-name">uint64</span>)(<span class="hljs-name">&current_thread->context</span>))<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>这个实验比较简单,但是当时做的时候最开始没有看到yield函数里面有一个设置runnable的操作了,在switch里自己还手动加了一个<code>current_thread->state = RUNNABLE;</code>,这样就导致最后只输出了一个c 0,程序就卡住了。我怀疑应该是只有在abc之间切换时才调用thread_yield,这时候才需要改变状态,所以thread_yield里有修改状态的操作,但是我在thread_switch里加了这个赋值操作之后就导致main的线程也被我关掉了,所以可能就导致直接切到了main函数里面然后退出了吧。</p>]]></content>
<summary type="html"><p><img src="/../picture/6.s081lab6multi/star.jpg" alt="换换口味,来个白金之星("></p>
<h1 id="Multithreading"><a href="#Multithreading" class="headerli</summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>6-s081-lab4-Traps</title>
<link href="http://example.com/2023/03/31/6-s081-lab4-Traps/"/>
<id>http://example.com/2023/03/31/6-s081-lab4-Traps/</id>
<published>2023-03-31T06:37:44.000Z</published>
<updated>2023-03-31T06:42:09.401Z</updated>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>6-s081-lab9-File-System</title>
<link href="http://example.com/2023/03/29/6-s081-lab9-File-System/"/>
<id>http://example.com/2023/03/29/6-s081-lab9-File-System/</id>
<published>2023-03-29T10:46:32.000Z</published>
<updated>2023-03-31T06:41:18.949Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/6.s081lab9fs/takagi.jpg" alt="镇楼"></p><h1 id="File-System"><a href="#File-System" class="headerlink" title="File System"></a>File System</h1><p>为什么这篇在其他实验之前呢,因为在这个实验上卡了挺长时间,现在做完正好一口气稍微整理下。其他实验可能看完xv6 book几个小时就搞定了,这个反复看了两遍xv6 book加上看了好久的源码才下手来做,趁着做完赶紧记录下来。顺便新建之前几个lab的文件夹,有时间再往里面填。</p><h2 id="文件系统简介(粗略讲讲)"><a href="#文件系统简介(粗略讲讲)" class="headerlink" title="文件系统简介(粗略讲讲)"></a>文件系统简介(粗略讲讲)</h2><p><img src="/../picture/6.s081lab9fs/fs_layout.png" alt="layout of file system"><br>文件系统大概分这些层,xv6 book大概就是按照自底向上的顺序来介绍。<br>首先是disk,xv6的实验用了virtio模拟了一个磁盘,接口就是按块读写的接口<br>然后是Buffer cache,这是非常重要的一个结构,通常来说从disk上读的数据都要放到这里,然后再去进一步被使用。在这个实验里buf的大小和block的大小是相同的,不知道别的文件系统设计里是否会不同。buffer cache的主要目的是同步对磁盘的读写,一次只让一个线程用这个文件的内容<br>接下来是logging layer(感觉这一层很神奇)。这一层主要的目的就是为了崩溃恢复,这里的崩溃恢复主要指的是在write的过程中,当crash发生后,可能会导致一个inode指向已经free的节点,或是一个已经是allocated但是实际上并没有被使用的block。这里我最开始其实理解有一些偏差,xv6中的logging实现方法是完全记录下整个被修改块,也即block/buffer本身,当这些积攒到一定数量后再整体落盘,这样就能确定这些操作都是原子性的,如果落盘成功,那么就算崩溃了也能还原,如果不成功,那么就当没有过这些操作,通过这样的手段,就能够保证系统层面的一致性,防止后续运行出错。<br>再然后是inode layer,这个应该非常熟悉了,文件系统用inode记录文件或者目录的属性,两者的区别就是文件在block里存文件数据,目录在block里存目录条目dirent,这个条目再指向下一个inode<br><img src="/../picture/6.s081lab9fs/inode.png" alt="这个图就是很经典的inode的结构图"><br>之后是directory layer,因为实现和文件的inode很像,这里就暂时先跳过了<br>最后就是File descriptor layer,这一层向外暴露出了文件系统的接口,所以文件系统才能方便地使用。每个文件在这个系统里都用结构体struct file来表示。每个进程有一个自己的已经打开的文件的table,而且所有的已经打开的文件在系统中有一个全局的ftable。<br>这部分内容非常多,反复看了xv6 book中的这个章节加代码才有点了解,但是感觉很多细节还是理解不够或是记不住,后面有时间再看一下。</p><h2 id="Large-Files"><a href="#Large-Files" class="headerlink" title="Large Files"></a>Large Files</h2><p>这个实验的目的是提高xv6最大文件的大小。因为现在只有一个indirect节点,所以一个文件最多只有268个block,这在很多情况下不够用。这个实验里再把一个direct block number改成doubly-indirect block,这样能提高到65803个block。<br>这个lab主要需要修改的就是bmap()和itrunc(),这两个函数的作用是遍历文件和销毁文件,所以修改了inode结构之后这两个函数也要相应地变化。这个lab不难,但是有个地方不小心写了一个不太容易察觉的bug,最后查了一下午才找到。<br>稍微修改下定义</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> NDIRECT 11</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NINDIRECT (BSIZE / sizeof(uint))</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NINDIRECT2 NINDIRECT*NINDIRECT</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> MAXFILE (NDIRECT + NINDIRECT + NINDIRECT2)</span><br></code></pre></td></tr></table></figure><p>因为NDIRECT变了,所以inode和dinode要改成<code>uint addrs[NDIRECT+2];</code><br>bmap里面前半部分不用动,后半部分加一下两层映射的东西,这个也是仿照前面的内容写就可以了,比较简单。这里最开始<code>(addr = a[bn/NINDIRECT])==0</code>写成了<code>addr = a[bn/NINDIRECT]==0</code>所以一直出错,最后一句句对照才找到问题。</p><figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs xl">bn -= NINDIRECT;<br> <span class="hljs-comment">//printf("finish 2\n");</span><br> <span class="hljs-keyword">if</span>(bn < NINDIRECT2){<br> <span class="hljs-comment">//printf("in 3\n");</span><br> <span class="hljs-comment">//这最开始还抄错了</span><br> <span class="hljs-function"><span class="hljs-title">if</span>((addr = ip-></span>addrs[NDIRECT+<span class="hljs-number">1</span>]) == <span class="hljs-number">0</span>){<br> <span class="hljs-comment">//printf("step1\n");</span><br> <span class="hljs-function"><span class="hljs-title">ip</span>-></span><span class="hljs-function"><span class="hljs-title">addrs</span>[NDIRECT+1] = addr = balloc(ip-></span>dev);<br> }<br> <span class="hljs-function"><span class="hljs-title">bp</span> = bread(ip-></span>dev, addr);<br> <span class="hljs-comment">//printf("in double");</span><br> <span class="hljs-function"><span class="hljs-title">a</span> = (uint*)bp-></span><span class="hljs-keyword">data</span>;<br> <span class="hljs-comment">//等号写错位置了,写代码20分钟debug两个小时,人晕了。。</span><br> <span class="hljs-keyword">if</span>((addr = a[bn/NINDIRECT])==<span class="hljs-number">0</span>){<br> <span class="hljs-comment">//printf("step2\n");</span><br> <span class="hljs-function"><span class="hljs-title">a</span>[bn/NINDIRECT] = addr = balloc(ip-></span>dev);<br> log_write(bp);<br> }<br> brelse(bp);<br> <span class="hljs-function"><span class="hljs-title">bp</span> = bread(ip-></span>dev, addr);<br> <span class="hljs-function"><span class="hljs-title">a</span> = (uint*)bp-></span><span class="hljs-keyword">data</span>;<br> <span class="hljs-keyword">if</span>((addr = a[bn%NINDIRECT])==<span class="hljs-number">0</span>){<br> <span class="hljs-comment">//printf("step3\n");</span><br> <span class="hljs-function"><span class="hljs-title">a</span>[bn%NINDIRECT] = addr = balloc(ip-></span>dev);<br> log_write(bp);<br> }<br> brelse(bp);<br> return addr;<br> }<br></code></pre></td></tr></table></figure><p>itruc和bmap几乎是一样的,注意细节就可以了。bread之后记得brelse</p><figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs xl"><span class="hljs-function"><span class="hljs-title">if</span>(ip-></span>addrs[NDIRECT+<span class="hljs-number">1</span>]){<br> <span class="hljs-comment">//这里漏了+1</span><br> <span class="hljs-function"><span class="hljs-title">bp</span> = bread(ip-></span><span class="hljs-function"><span class="hljs-title">dev</span>, ip-></span>addrs[NDIRECT+<span class="hljs-number">1</span>]);<br> <span class="hljs-function"><span class="hljs-title">a</span> = (uint*)bp-></span><span class="hljs-keyword">data</span>;<br> <span class="hljs-keyword">for</span>(k = <span class="hljs-number">0</span>; k < NINDIRECT; k++){<br> <span class="hljs-keyword">if</span>(a[k]){<br> <span class="hljs-function"><span class="hljs-title">bp_in_loop</span> = bread(ip-></span>dev, a[k]);<br> <span class="hljs-function"><span class="hljs-title">b</span> = (uint *)bp_in_loop-></span><span class="hljs-keyword">data</span>;<br> <span class="hljs-keyword">for</span>(l = <span class="hljs-number">0</span>;l<NINDIRECT;l++){<br> <span class="hljs-keyword">if</span>(b[l]){<br> <span class="hljs-function"><span class="hljs-title">bfree</span>(ip-></span>dev, b[l]);<br> <span class="hljs-comment">//b[l]=0;</span><br> }<br> }<br> brelse(bp_in_loop);<br> <span class="hljs-function"><span class="hljs-title">bfree</span>(ip-></span>dev, a[k]);<br> <span class="hljs-comment">//a[k]=0;</span><br> }<br> }<br> brelse(bp);<br> <span class="hljs-function"><span class="hljs-title">bfree</span>(ip-></span><span class="hljs-function"><span class="hljs-title">dev</span>, ip-></span>addrs[NDIRECT+<span class="hljs-number">1</span>]);<br> <span class="hljs-function"><span class="hljs-title">ip</span>-></span>addrs[NDIRECT+<span class="hljs-number">1</span>]=<span class="hljs-number">0</span>;<br> }<br></code></pre></td></tr></table></figure><p>这个实验总体还是非常简单的,就是细节比较多,有可能在细节上出问题</p><h2 id="Symbolic-links"><a href="#Symbolic-links" class="headerlink" title="Symbolic links"></a>Symbolic links</h2><p>这个实验的目的是加符号链接(也即软链接),通常所说的硬链接,是指一个新的文件指向旧的文件的inode,这个方法有一定局限性,如果inode在另一个设备上就无法链接。而软链接是把要链接的目标的路径保存在软链接文件里,打开软链接文件时通过这个路径递归打开指向的文件。<br>加系统调用应该轻车熟路了,这里就不赘述了,主要讲讲sym_link()怎么实现,以及open里该实现什么功能。<br>参考别的系统调用,可以直接create一个T_SYMLINK类型的文件,然后用writei往第一个数据块写入链接的路径。这里最开始没想明白直接存到文件里,还以为要从inode里找地方,后来发现inode牵扯太多重读了xv6 book这一章才大概想明白怎么做。create返回的inode带锁,所以write完之后需要unlockput,不然单线程测试的时候会因为锁争用一直卡在这里。</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs reasonml">uint64 sys<span class="hljs-constructor">_symlink(<span class="hljs-params">void</span>)</span>{<br> <span class="hljs-built_in">char</span> target<span class="hljs-literal">[<span class="hljs-number">64</span>]</span>, path<span class="hljs-literal">[<span class="hljs-number">64</span>]</span>;<br> <span class="hljs-keyword">struct</span> inode *ip;<br> <span class="hljs-built_in">int</span> n = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">if</span>((n = argstr(<span class="hljs-number">0</span>, target, <span class="hljs-number">64</span>)) < <span class="hljs-number">0</span><span class="hljs-operator"> || </span>argstr(<span class="hljs-number">1</span>, path, <span class="hljs-number">64</span>) < <span class="hljs-number">0</span>)<br> return -<span class="hljs-number">1</span>;<br> <span class="hljs-keyword">begin</span><span class="hljs-constructor">_op()</span>;<br> <span class="hljs-keyword">if</span>((ip=create(path,T_SYMLINK,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))==<span class="hljs-number">0</span>){<br> <span class="hljs-keyword">end</span><span class="hljs-constructor">_op()</span>;<br> return -<span class="hljs-number">1</span>;<br> }<br><br> <span class="hljs-keyword">if</span>(writei(ip,<span class="hljs-number">0</span>,(uint64)target,<span class="hljs-number">0</span>,n)<<span class="hljs-number">0</span>){<br> <span class="hljs-keyword">end</span><span class="hljs-constructor">_op()</span>;<br> return -<span class="hljs-number">1</span>;<br> }<br> <span class="hljs-comment">//这里最开始没注意到create后的东西是有lock的,所以单线程的时候这里不释放会卡到后面的操作</span><br> iunlockput(ip);<br> <span class="hljs-keyword">end</span><span class="hljs-constructor">_op()</span>;<br> return <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>open函数里需要加上文件为软链接文件的情况,大体分为两种:</p><ul><li>正常情况下,打开一个软链接文件时,应该递归打开软链接文件里保存的路径,直到打开一个其他类型的文件</li><li>如果open的参数指定了O_NOFOLLOW,那就不递归,直接返回软链接文件本身,也就是返回的文件描述符指向的文件的inode就是软链接文件的inode<br>所以设计思路就是一个大循环,判断inode是不是T_SYMLINK类型,同时计数,计数超过十失败,如果指定了O_NOFOLLOW就直接break。除此之外就是递归找下一个链接文件的部分,先用readi读出路径,然后namei找到对应的inode,进入下一次循环。这个环节中记得加锁和释放锁,不然多线程测试会出错。<figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs axapta"><span class="hljs-built_in">int</span> <span class="hljs-keyword">count</span> = <span class="hljs-number">1</span>;<br><span class="hljs-keyword">while</span>(ip->type == T_SYMLINK){<br> <span class="hljs-keyword">count</span>++;<br> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">count</span>><span class="hljs-number">10</span>){<br> iunlockput(ip);<br> end_op();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> }<br> <span class="hljs-keyword">if</span>(omode & O_NOFOLLOW){<br> <span class="hljs-keyword">break</span>;<br> }<span class="hljs-keyword">else</span>{<br> readi(ip,<span class="hljs-number">0</span>,(uint64)path,<span class="hljs-number">0</span>,MAXPATH);<br> iunlockput(ip);<br> <span class="hljs-keyword">if</span>((ip = namei(path)) == <span class="hljs-number">0</span>){<br> <span class="hljs-comment">//这里最开始随便加了,看到panic unlock才想明白这里代表没有分配inode,当然是不能直接unlock的</span><br> <span class="hljs-comment">//iunlockput(ip);</span><br> end_op();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> }<br> ilock(ip);<br> }<br>}<br></code></pre></td></tr></table></figure></li></ul><p>这个实验代码量虽然不多,但是通读指导书以及理解每个函数的作用用的时间还是挺久的,断断续续做了差不多两周吧。感觉这个实验是挺有意思的一个实验。到这里部分想做的实验差不多做完了,虽然有些没做但是也不太想搞了,后续如果有想再做一下的实验可能会回来看一下,总的来说感觉xv6的实验设计还是挺用心的,系统也挺适合初学者来学习的。</p>]]></content>
<summary type="html"><p><img src="/../picture/6.s081lab9fs/takagi.jpg" alt="镇楼"></p>
<h1 id="File-System"><a href="#File-System" class="headerlink" title="File S</summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
<entry>
<title>论文阅读 The Design and Implementation of a Log-Structured File System</title>
<link href="http://example.com/2023/03/27/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB-The-Design-and-Implementation-of-a-Log-Structured-File-System/"/>
<id>http://example.com/2023/03/27/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB-The-Design-and-Implementation-of-a-Log-Structured-File-System/</id>
<published>2023-03-27T11:45:02.000Z</published>
<updated>2023-04-01T08:20:36.114Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/LFS-Reading/takagi.jpg" alt="可可爱爱三人组"></p><h1 id="The-Design-and-Implementation-of-a-Log-Structured-File-System"><a href="#The-Design-and-Implementation-of-a-Log-Structured-File-System" class="headerlink" title="The Design and Implementation of a Log-Structured File System"></a>The Design and Implementation of a Log-Structured File System</h1><p>网上的关于LFS的资料有点松散了,看到最后感觉还是得追根溯源来看看这篇论文</p><h2 id="看到一半的中途更新"><a href="#看到一半的中途更新" class="headerlink" title="看到一半的中途更新"></a>看到一半的中途更新</h2><p>这篇论文虽然是我目前找到的几乎可以说是最久远的文献了,但是很多地方介绍的不是非常详细,对于初学者来说不是很友好,前段时间偶然间看到了一篇很不错的文章:<a href="https://pages.cs.wisc.edu/~remzi/OSTEP/file-lfs.pdf">https://pages.cs.wisc.edu/~remzi/OSTEP/file-lfs.pdf</a><br>这个文章对于整体结构的描述非常详细,结合着来看这篇文章感觉收获很大!</p><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><blockquote><p>Log-structured file systems are based on the assumption that files are cached in main memory and that increasing memory sizes will make the caches more and more effective at satisfying read requests[1]. As a result, disk traffic will become dominated by writes.</p></blockquote><p>这个文章是基于这样一种假设:如果cache够大,那么所有读请求都会被cache满足,那么硬盘层面的访问延迟就都会出现在写请求上。从现在的角度来看,SSD访问的延迟方面写延迟是远高于读延迟的,也就是说为了满足SSD的访问需要,通过这种方式构建文件系统减少随机写也是合理的。这篇文章就提出了这样的一个文件系统,叫做Sprite LFS <del>雪碧日志文件系统</del></p><h2 id="Design-for-file-systems-of-the-1990’s"><a href="#Design-for-file-systems-of-the-1990’s" class="headerlink" title="Design for file systems of the 1990’s"></a>Design for file systems of the 1990’s</h2><p>其实这段就是背景介绍</p><h3 id="2-1-Technology"><a href="#2-1-Technology" class="headerlink" title="2.1 Technology"></a>2.1 Technology</h3><p>三个部分对文件系统设计非常重要:processors,disks,main memory.</p><ul><li><p>Processors增速太快,导致其他部分必须尽力够得上它的增长速度。</p></li><li><p>Disk也在快速发展,但是发展主要在cost和capacity上,而不是性能上。Disk performance分为两部分,transfer bandwidth和access time。</p><blockquote><p>Disk transfer bandwidth can be improved substantially with the use of disk arrays and parallel-head disks[5] but no major improvements seem likely for access time (it is determined by mechanical motions that are hard to improve). If an application causes a sequence of small disk transfers separated by seeks, then the application is not likely to experience much speedup over the next ten years, even with faster processors.</p></blockquote></li><li><p>main memory的容量在指数级增长,这导致可以使用更大的file cache</p><blockquote><p>First, larger file caches alter the workload presented to the disk by absorbing a greater fraction of the read requests[1, 6]. Most write requests must eventually be reflected on disk for safety, so disk traffic (and disk performance) will become more and more dominated by writes.</p></blockquote></li></ul><p>这点就跟上面提到的一样,cache大了对read利好,这样performance就很大程度上依赖于write了</p><blockquote><p>The second impact of large file caches is that they can serve as write buffers where large numbers of modified blocks can be collected before writing any of them to disk. Buffering may make it possible to write the blocks more efficiently, for example by writing them all in a single sequential transfer with only one seek.</p></blockquote><p>第二点就是可以把多个写请求汇总到一个write buffer里,这样能够减少写的访问磁盘的次数</p><h3 id="2-2-Workloads"><a href="#2-2-Workloads" class="headerlink" title="2.2 Workloads"></a>2.2 Workloads</h3><p>工作负载有两种,小的增删改的场景是LFS的优化场景,大文件的这里提到更侧重硬件方面的设计,但是LFS在大文件上表现也很好</p><blockquote><p>Small files usually result in small random disk I/Os, and the creation and deletion times for such files are often dominated by updates to file system ‘‘metadata’’ (the data structures used to locate the attributes and blocks of the file).</p></blockquote><p>小文件增删的时间通常是由metadata(我觉得应该包含创建inode,把inode加到全局表,大概就这两个吧)决定的</p><h3 id="2-3-Problems-with-existing-file-systems"><a href="#2-3-Problems-with-existing-file-systems" class="headerlink" title="2.3 Problems with existing file systems"></a>2.3 Problems with existing file systems</h3><ul><li>以Unix FFS为例,每次创建文件的时候需要访问两次文件属性,文件数据、目录数据和目录属性又需要分别访问一次磁盘。在这样的系统写小文件的时候只有少于5%的磁盘带宽被使用了,其他时间都在寻道。</li><li>现有的文件系统尝试去同步地写(这里没太看懂,为了一致性不是必须要这样吗?看后面怎么解释了):应用等待写完成,而不是把写挂在后台完成。</li></ul><p>看到这里感觉这个motivation讲的还是挺明确的,就是为了解决small size write的问题。</p><h2 id="Log-structured-file-systems"><a href="#Log-structured-file-systems" class="headerlink" title="Log-structured file systems"></a>Log-structured file systems</h2>]]></content>
<summary type="html"><p><img src="/../picture/LFS-Reading/takagi.jpg" alt="可可爱爱三人组"></p>
<h1 id="The-Design-and-Implementation-of-a-Log-Structured-File-System"><</summary>
<category term="Reading" scheme="http://example.com/tags/Reading/"/>
</entry>
<entry>
<title>6-s081-lab3 Page Table</title>
<link href="http://example.com/2023/03/24/6-s081-lab3-Page-Table/"/>
<id>http://example.com/2023/03/24/6-s081-lab3-Page-Table/</id>
<published>2023-03-24T14:38:22.000Z</published>
<updated>2023-03-27T11:55:22.860Z</updated>
<content type="html"><![CDATA[<p><img src="/../picture/6.s081lab3/takagi.jpg" alt="高木镇楼(什么时候我能自己画出这样的画就好了)"></p><h1 id="Page-Table"><a href="#Page-Table" class="headerlink" title="Page Table"></a>Page Table</h1>]]></content>
<summary type="html"><p><img src="/../picture/6.s081lab3/takagi.jpg" alt="高木镇楼(什么时候我能自己画出这样的画就好了)"></p>
<h1 id="Page-Table"><a href="#Page-Table" class="headerli</summary>
<category term="OS" scheme="http://example.com/tags/OS/"/>
</entry>
</feed>