編譯原理棧緩沖區與句型的關系
㈠ Linux內核設計與實現的目錄
譯者序
序言
前言
作者簡介
第1章Linux內核簡介1
1.1Unix的歷史1
1.2追尋Linus足跡:Linux簡介2
1.3操作系統和內核簡介3
1.4Linux內核和傳統Unix內核的比較5
1.5Linux內核版本7
1.6Linux內核開發者社區8
1.7小結8
第2章從內核出發10
2.1獲取內核源碼10
2.1.1使用Git10
2.1.1安裝內核源代碼10
2.1.3使用補丁11
2.2內核源碼樹11
2.3編譯內核12
2.3.1配置內核12
2.3.2減少編譯的垃圾信息14
2.3.3衍生多個編譯作業 14
2.3.4安裝新內核14
2.4內核開發的特點15
2.4.1無libc庫抑或無標准頭文件15
2.4.2GNU C16
2.4.3沒有內存保護機制18
2.4.4不要輕易在內核中使用浮點數18
2.4.5容積小而固定的棧18
2.4.6同步和並發18
2.4.7可移植性的重要性19
2.5小結19
第3章進程管理20
3.1進程20
3.2進程描述符及任務結構 21
3.2.1分配進程描述符22
3.2.2進程描述符的存放23
3.2.3進程狀態23
3.2.4設置當前進程狀態25
3.2.5進程上下文25
3.2.6進程家族樹25
3.3進程創建26
3.3.1寫時拷貝27
3.3.2fork()27
3.3.3vfork()28
3.4線程在Linux中的實現28
3.4.1創建線程29
3.4.2內核線程30
3.5進程終結31
3.5.1刪除進程描述符32
3.5.2孤兒進程造成的進退維谷32
3.6小結34
第4章進程調度35
4.1多任務35
4.2Linux 的進程調度36
4.3策略36
4.3.1I/O消耗型和處理器消耗型的進程36
4.3.2進程優先順序37
4.3.3時間片38
4.3.4調度策略的活動38
4.4Linux調度演算法39
4.4.1調度器類39
4.4.2Unix 系統中的進程調度40
4.4.3公平調度41
4.5Linux調度的實現42
4.5.1時間記賬42
4.5.2進程選擇44
4.5.3調度器入口48
4.5.4睡眠和喚醒49
4.6搶占和上下文切換51
4.6.1用戶搶佔53
4.6.2內核搶佔53
4.7實時調度策略54
4.8與調度相關的系統調用54
4.8.1與調度策略和優先順序相關的系統調用55
4.8.2與處理器綁定有關的系統調用55
4.8.3放棄處理器時間56
4.9小結56
第5章系統調用57
5.1與內核通信57
5.2API、POSIX和C庫57
5.3系統調用58
5.3.1系統調用號59
5.3.2系統調用的性能59
5.4系統調用處理程序60
5.4.1指定恰當的系統調用60
5.4.2參數傳遞60
5.5系統調用的實現61
5.5.1實現系統調用61
5.5.2參數驗證62
5.6系統調用上下文64
5.6.1綁定一個系統調用的最後步驟65
5.6.2從用戶空間訪問系統調用67
5.6.3為什麼不通過系統調用的方式實現68
5.7小結68
第6章內核數據結構69
6.1鏈表69
6.1.1單向鏈表和雙向鏈表69
6.1.2環形鏈表70
6.1.3沿鏈表移動71
6.1.4Linux 內核中的實現71
6.1.5操作鏈表73
6.1.6遍歷鏈表75
6.2隊列78
6.2.1kfifo79
6.2.2創建隊列79
6.2.3推入隊列數據79
6.2.4摘取隊列數據80
6.2.5獲取隊列長度80
6.2.6重置和撤銷隊列80
6.2.7隊列使用舉例 81
6.3映射 81
6.3.1初始化一個idr82
6.3.2分配一個新的UID82
6.3.3查找UID83
6.3.4刪除UID84
6.3.5撤銷idr84
6.4二叉樹84
6.4.1二叉搜索樹84
6.4.2自平衡二叉搜索樹 85
6.5數據結構以及選擇 87
6.6演算法復雜度88
6.6.1演算法88
6.6.2大o 符號88
6.6.3大θ符號89
6.6.4時間復雜度89
6.7小結 90
第7章中斷和中斷處理91
7.1中斷91
7.2中斷處理程序92
7.3上半部與下半部的對比93
7.4注冊中斷處理程序93
7.4.1中斷處理程序標志94
7.4.2一個中斷例子95
7.4.3釋放中斷處理程序95
7.5編寫中斷處理程序96
7.5.1共享的中斷處理程序97
7.5.2中斷處理程序實例97
7.6中斷上下文99
7.7中斷處理機制的實現100
7.8/proc/interrupts102
7.9中斷控制103
7.9.1禁止和激活中斷103
7.9.2禁止指定中斷線105
7.9.3中斷系統的狀態105
7.10小結106
第8章下半部和推後執行的工作107
8.1下半部107
8.1.1為什麼要用下半部108
8.1.2下半部的環境108
8.2軟中斷110
8.2.1軟中斷的實現111
8.2.2使用軟中斷113
8.3tasklet114
8.3.1tasklet的實現114
8.3.2使用tasklet116
8.3.3老的BH機制119
8.4工作隊列120
8.4.1工作隊列的實現121
8.4.2使用工作隊列124
8.4.3老的任務隊列機制126
8.5下半部機制的選擇127
8.6在下半部之間加鎖128
8.7禁止下半部128
8.8小結129
第9章內核同步介紹131
9.1臨界區和競爭條件131
9.1.1為什麼我們需要保護132
9.1.2單個變數133
9.2加鎖134
9.2.1造成並發執行的原因135
9.2.2了解要保護些什麼136
9.3死鎖137
9.4爭用和擴展性138
9.5小結140
第10章內核同步方法141
10.1原子操作141
10.1.1原子整數操作142
10.1.264位原子操作144
10.1.3原子位操作145
10.2自旋鎖147
10.2.1自旋鎖方法148
10.2.2其他針對自旋鎖的操作149
10.2.3自旋鎖和下半部150
10.3讀-寫自旋鎖150
10.4信號量152
10.4.1計數信號量和二值信號量153
10.4.2創建和初始化信號量154
10.4.3使用信號量154
10.5讀-寫信號量155
10.6互斥體156
10.6.1信號量和互斥體158
10.6.2自旋鎖和互斥體158
10.7完成變數158
10.8BLK:大內核鎖159
10.9順序鎖160
10.10禁止搶佔161
10.11順序和屏障162
10.12小結165
第11章定時器和時間管理166
11.1內核中的時間概念166
11.2節拍率:HZ167
11.2.1理想的HZ值168
11.2.2高HZ的優勢169
11.2.3高HZ的劣勢169
11.3jiffies170
11.3.1jiffies的內部表示171
11.3.2jiffies 的回繞172
11.3.3用戶空間和HZ173
11.4硬時鍾和定時器174
11.4.1實時時鍾174
11.4.2系統定時器174
11.5時鍾中斷處理程序174
11.6實際時間176
11.7定時器178
11.7.1使用定時器178
11.7.2定時器競爭條件180
11.7.3實現定時器180
11.8延遲執行181
11.8.1忙等待181
11.8.2短延遲182
11.8.3schele_timeout()183
11.9小結185
第12章內存管理186
12.1頁186
12.2區187
12.3獲得頁189
12.3.1獲得填充為0的頁190
12.3.2釋放頁191
12.4kmalloc()191
12.4.1gfp_mask標志192
12.4.2kfree()195
12.5vmalloc()196
12.6slab層197
12.6.1slab層的設計198
12.6.2slab分配器的介面200
12.7在棧上的靜態分配203
12.7.1單頁內核棧203
12.7.2在棧上光明正大地工作203
12.8高端內存的映射204
12.8.1永久映射204
12.8.2臨時映射204
12.9每個CPU的分配20512.10新的每個CPU介面206
12.10.1編譯時的每個CPU數據206
12.10.2運行時的每個CPU數據207
12.11使用每個CPU數據的原因208
12.12分配函數的選擇209
12.13小結209
第13章虛擬文件系統210
13.1通用文件系統介面210
13.2文件系統抽象層211
13.3Unix文件系統212
13.4VFS 對象及其數據結構213
13.5超級塊對象214
13.6超級塊操作215
13.7索引節點對象217
13.8索引節點操作219
13.9目錄項對象222
13.9.1目錄項狀態222
13.9.2目錄項緩存223
13.10目錄項操作224
13.11文件對象225
13.12文件操作226
13.13和文件系統相關的數據結構230
13.14和進程相關的數據結構232
13.15小結233
第14章塊I/O層234
14.1剖析一個塊設備234
14.2緩沖區和緩沖區頭235
14.3bio結構體237
14.3.1I/O向量238
14.3.2新老方法對比239
14.4請求隊列240
14.5I/O調度程序240
14.5.1I/O調度程序的工作241
14.5.2Linus 電梯241
14.5.3最終期限I/O調度程序242
14.5.4預測I/O調度程序244
14.5.5完全公正的排隊I/O調度程序244
14.5.6空操作的I/O調度程序245
14.5.7I/O調度程序的選擇245
14.6小結246
第15章進程地址空間247
15.1地址空間247
15.2內存描述符248
15.2.1分配內存描述符249
15.2.2撤銷內存描述符250
15.2.3mm_struct 與內核線程250
15.3虛擬內存區域251
15.3.1VMA標志251
15.3.2VMA 操作253
15.3.3內存區域的樹型結構和內存區域的鏈表結構254
15.3.4實際使用中的內存區域254
15.4操作內存區域255
15.4.1find_vma()256
15.4.2find_vma_prev()257
15.4.3find_vma_intersection()257
15.5mmap()和do_mmap():創建地址區間258
15.6mummap()和do_mummap():刪除地址區間259
15.7頁表260
15.8小結261
第16章頁高速緩存和頁回寫262
16.1緩存手段262
16.1.1寫緩存262
16.1.2緩存回收263
16.2Linux 頁高速緩存264
16.2.1address_space對象264
16.2.2address_space 操作266
16.2.3基樹267
16.2.4以前的頁散列表268
16.3緩沖區高速緩存268
16.4flusher線程268
16.4.1膝上型計算機模式270
16.4.2歷史上的bdflush、kupdated 和pdflush270
16.4.3避免擁塞的方法:使用多線程271
16.5小結271
第17章設備與模塊273
17.1設備類型273
17.2模塊274
17.2.1Hello,World274
17.2.2構建模塊275
17.2.3安裝模塊277
17.2.4產生模塊依賴性277
17.2.5載入模塊278
17.2.6管理配置選項279
17.2.7模塊參數280
17.2.8導出符號表282
17.3設備模型283
17.3.1kobject283
17.3.2ktype284
17.3.3kset285
17.3.4kobject、ktype和kset的相互關系285
17.3.5管理和操作kobject286
17.3.6引用計數287
17.4sysfs288
17.4.1sysfs中添加和刪除kobject 290
17.4.2向sysfs中添加文件291
17.4.3內核事件層293
17.5小結294
第18章調試295
18.1准備開始295
18.2內核中的bug296
18.3通過列印來調試296
18.3.1健壯性296
18.3.2日誌等級297
18.3.3記錄緩沖區298
18.3.4syslogd和klogd298
18.3.5從printf()到printk()的轉換298
18.4oops298
18.4.1ksymoops300
18.4.2kallsyms300
18.5內核調試配置選項301
18.6引發bug並列印信息301
18.7神奇的系統請求鍵302
18.8內核調試器的傳奇303
18.8.1gdb303
18.8.2kgdb304
18.9探測系統304
18.9.1用UID作為選擇條件304
18.9.2使用條件變數305
18.9.3使用統計量305
18.9.4重復頻率限制305
18.10用二分查找法找出引發罪惡的變更306
18.11使用Git進行二分搜索307
18.12當所有的努力都失敗時:社區308
18.13小結308
第19章可移植性309
19.1可移植操作系統309
19.2Linux移植史310
19.3字長和數據類型311
19.3.1不透明類型313
19.3.2指定數據類型314
19.3.3長度明確的類型314
19.3.4char型的符號問題315
19.4數據對齊315
19.4.1避免對齊引發的問題316
19.4.2非標准類型的對齊316
19.4.3結構體填補316
19.5位元組順序318
19.6時間319
19.7頁長度320
19.8處理器排序320
19.9SMP、內核搶占、高端內存321
19.10小結321
第20章補丁、開發和社區322
20.1社區322
20.2Linux編碼風格322
20.2.1縮進323
20.2.2switch 語句323
20.2.3空格324
20.2.4花括弧325
20.2.5每行代碼的長度326
20.2.6命名規范326
20.2.7函數326
20.2.8注釋326
20.2.9typedef327
20.2.10多用現成的東西328
20.2.11在源碼中減少使用ifdef328
20.2.12結構初始化328
20.2.13代碼的事後修正329
20.3管理系統329
20.4提交錯誤報告329
20.5補丁330
20.5.1創建補丁330
20.5.2用Git創建補丁331
20.5.3提交補丁331
20.6小結332
參考資料333
㈡ 如何用PyTorch實現遞歸神經網路
從 Siri 到谷歌翻譯,深度神經網路已經在機器理解自然語言方面取得了巨大突破。這些模型大多數將語言視為單調的單詞或字元序列,並使用一種稱為循環神經網路(recurrent neural network/RNN)的模型來處理該序列。但是許多語言學家認為語言最好被理解為具有樹形結構的層次化片語,一種被稱為遞歸神經網路(recursive neural network)的深度學習模型考慮到了這種結構,這方面已經有大量的研究。雖然這些模型非常難以實現且效率很低,但是一個全新的深度學習框架 PyTorch 能使它們和其它復雜的自然語言處理模型變得更加容易。
雖然遞歸神經網路很好地顯示了 PyTorch 的靈活性,但它也廣泛支持其它的各種深度學習框架,特別的是,它能夠對計算機視覺(computer vision)計算提供強大的支撐。PyTorch 是 Facebook AI Research 和其它幾個實驗室的開發人員的成果,該框架結合了 Torch7 高效靈活的 GPU 加速後端庫與直觀的 Python 前端,它的特點是快速成形、代碼可讀和支持最廣泛的深度學習模型。
開始 SPINN
鏈接中的文章(https://github.com/jekbradbury/examples/tree/spinn/snli)詳細介紹了一個遞歸神經網路的 PyTorch 實現,它具有一個循環跟蹤器(recurrent tracker)和 TreeLSTM 節點,也稱為 SPINN——SPINN 是深度學習模型用於自然語言處理的一個例子,它很難通過許多流行的框架構建。這里的模型實現部分運用了批處理(batch),所以它可以利用 GPU 加速,使得運行速度明顯快於不使用批處理的版本。
SPINN 的意思是堆棧增強的解析器-解釋器神經網路(Stack-augmented Parser-Interpreter Neural Network),由 Bowman 等人於 2016 年作為解決自然語言推理任務的一種方法引入,該論文中使用了斯坦福大學的 SNLI 數據集。
該任務是將語句對分為三類:假設語句 1 是一幅看不見的圖像的准確標題,那麼語句 2(a)肯定(b)可能還是(c)絕對不是一個准確的標題?(這些類分別被稱為蘊含(entailment)、中立(neutral)和矛盾(contradiction))。例如,假設一句話是「兩只狗正跑過一片場地」,蘊含可能會使這個語句對變成「戶外的動物」,中立可能會使這個語句對變成「一些小狗正在跑並試圖抓住一根棍子」,矛盾能會使這個語句對變成「寵物正坐在沙發上」。
特別地,研究 SPINN 的初始目標是在確定語句的關系之前將每個句子編碼(encoding)成固定長度的向量表示(也有其它方式,例如注意模型(attention model)中將每個句子的每個部分用一種柔焦(soft focus)的方法相互比較)。
數據集是用句法解析樹(syntactic parse tree)方法由機器生成的,句法解析樹將每個句子中的單詞分組成具有獨立意義的短語和子句,每個短語由兩個詞或子短語組成。許多語言學家認為,人類通過如上面所說的樹的分層方式來組合詞意並理解語言,所以用相同的方式嘗試構建一個神經網路是值得的。下面的例子是數據集中的一個句子,其解析樹由嵌套括弧表示:
( ( The church ) ( ( has ( cracks ( in ( the ceiling ) ) ) ) . ) )
這個句子進行編碼的一種方式是使用含有解析樹的神經網路構建一個神經網路層 Rece,這個神經網路層能夠組合詞語對(用詞嵌入(word embedding)表示,如 GloVe)、 和/或短語,然後遞歸地應用此層(函數),將最後一個 Rece 產生的結果作為句子的編碼:
X = Rece(「the」, 「ceiling」)
Y = Rece(「in」, X)
... etc.
但是,如果我希望網路以更類似人類的方式工作,從左到右閱讀並保留句子的語境,同時仍然使用解析樹組合短語?或者,如果我想訓練一個網路來構建自己的解析樹,讓解析樹根據它看到的單詞讀取句子?這是一個同樣的但方式略有不同的解析樹的寫法:
The church ) has cracks in the ceiling ) ) ) ) . ) )
或者用第 3 種方式表示,如下:
WORDS: The church has cracks in the ceiling .
PARSES: S S R S S S S S R R R R S R R
我所做的只是刪除開括弧,然後用「S」標記「shift」,並用「R」替換閉括弧用於「rece」。但是現在可以從左到右讀取信息作為一組指令來操作一個堆棧(stack)和一個類似堆棧的緩沖區(buffer),能得到與上述遞歸方法完全相同的結果:
1. 將單詞放入緩沖區。
2. 從緩沖區的前部彈出「The」,將其推送(push)到堆棧上層,緊接著是「church」。
3. 彈出前 2 個堆棧值,應用於 Rece,然後將結果推送回堆棧。
4. 從緩沖區彈出「has」,然後推送到堆棧,然後是「cracks」,然後是「in」,然後是「the」,然後是「ceiling」。
5. 重復四次:彈出 2 個堆棧值,應用於 Rece,然後推送結果。
6. 從緩沖區彈出「.」,然後推送到堆棧上層。
7. 重復兩次:彈出 2 個堆棧值,應用於 Rece,然後推送結果。
8. 彈出剩餘的堆棧值,並將其作為句子編碼返回。
我還想保留句子的語境,以便在對句子的後半部分應用 Rece 層時考慮系統已經讀取的句子部分的信息。所以我將用一個三參數函數替換雙參數的 Rece 函數,該函數的輸入值為一個左子句、一個右子句和當前句的上下文狀態。該狀態由神經網路的第二層(稱為循環跟蹤器(Tracker)的單元)創建。Tracker 在給定當前句子上下文狀態、緩沖區中的頂部條目 b 和堆棧中前兩個條目 s1\s2 時,在堆棧操作的每個步驟(即,讀取每個單詞或閉括弧)後生成一個新狀態:
context[t+1] = Tracker(context[t], b, s1, s2)
容易設想用你最喜歡的編程語言來編寫代碼做這些事情。對於要處理的每個句子,它將從緩沖區載入下一個單詞,運行跟蹤器,檢查是否將單詞推送入堆棧或執行 Rece 函數,執行該操作;然後重復,直到對整個句子完成處理。通過對單個句子的應用,該過程構成了一個大而復雜的深度神經網路,通過堆棧操作的方式一遍又一遍地應用它的兩個可訓練層。但是,如果你熟悉 TensorFlow 或 Theano 等傳統的深度學習框架,就知道它們很難實現這樣的動態過程。你值得花點時間回顧一下,探索為什麼 PyTorch 能有所不同。
圖論
圖 1:一個函數的圖結構表示
深度神經網路本質上是有大量參數的復雜函數。深度學習的目的是通過計算以損失函數(loss)度量的偏導數(梯度)來優化這些參數。如果函數表示為計算圖結構(圖 1),則向後遍歷該圖可實現這些梯度的計算,而無需冗餘工作。每個現代深度學習框架都是基於此反向傳播(backpropagation)的概念,因此每個框架都需要一個表示計算圖的方式。
在許多流行的框架中,包括 TensorFlow、Theano 和 Keras 以及 Torch7 的 nngraph 庫,計算圖是一個提前構建的靜態對象。該圖是用像數學表達式的代碼定義的,但其變數實際上是尚未保存任何數值的佔位符(placeholder)。圖中的佔位符變數被編譯進函數,然後可以在訓練集的批處理上重復運行該函數來產生輸出和梯度值。
這種靜態計算圖(static computation graph)方法對於固定結構的卷積神經網路效果很好。但是在許多其它應用中,有用的做法是令神經網路的圖結構根據數據而有所不同。在自然語言處理中,研究人員通常希望通過每個時間步驟中輸入的單詞來展開(確定)循環神經網路。上述 SPINN 模型中的堆棧操作很大程度上依賴於控制流程(如 for 和 if 語句)來定義特定句子的計算圖結構。在更復雜的情況下,你可能需要構建結構依賴於模型自身的子網路輸出的模型。
這些想法中的一些(雖然不是全部)可以被生搬硬套到靜態圖系統中,但幾乎總是以降低透明度和增加代碼的困惑度為代價。該框架必須在其計算圖中添加特殊的節點,這些節點代表如循環和條件的編程原語(programming primitive),而用戶必須學習和使用這些節點,而不僅僅是編程代碼語言中的 for 和 if 語句。這是因為程序員使用的任何控制流程語句將僅運行一次,當構建圖時程序員需要硬編碼(hard coding)單個計算路徑。
例如,通過詞向量(從初始狀態 h0 開始)運行循環神經網路單元(rnn_unit)需要 TensorFlow 中的特殊控制流節點 tf.while_loop。需要一個額外的特殊節點來獲取運行時的詞長度,因為在運行代碼時它只是一個佔位符。
# TensorFlow
# (this code runs once, ring model initialization)
# 「words」 is not a real list (it』s a placeholder variable) so
# I can』t use 「len」
cond = lambda i, h: i < tf.shape(words)[0]
cell = lambda i, h: rnn_unit(words[i], h)
i = 0
_, h = tf.while_loop(cond, cell, (i, h0))
基於動態計算圖(dynamic computation graph)的方法與之前的方法有根本性不同,它有幾十年的學術研究歷史,其中包括了哈佛的 Kayak、自動微分庫(autograd)以及以研究為中心的框架 Chainer和 DyNet。在這樣的框架(也稱為運行時定義(define-by-run))中,計算圖在運行時被建立和重建,使用相同的代碼為前向通過(forward pass)執行計算,同時也為反向傳播(backpropagation)建立所需的數據結構。這種方法能產生更直接的代碼,因為控制流程的編寫可以使用標準的 for 和 if。它還使調試更容易,因為運行時斷點(run-time breakpoint)或堆棧跟蹤(stack trace)將追蹤到實際編寫的代碼,而不是執行引擎中的編譯函數。可以在動態框架中使用簡單的 Python 的 for 循環來實現有相同變數長度的循環神經網路。
# PyTorch (also works in Chainer)
# (this code runs on every forward pass of the model)
# 「words」 is a Python list with actual values in it
h = h0
for word in words:
h = rnn_unit(word, h)
PyTorch 是第一個 define-by-run 的深度學習框架,它與靜態圖框架(如 TensorFlow)的功能和性能相匹配,使其能很好地適合從標准卷積神經網路(convolutional network)到最瘋狂的強化學習(reinforcement learning)等思想。所以讓我們來看看 SPINN 的實現。
代碼
在開始構建網路之前,我需要設置一個數據載入器(data loader)。通過深度學習,模型可以通過數據樣本的批處理進行操作,通過並行化(parallelism)加快訓練,並在每一步都有一個更平滑的梯度變化。我想在這里可以做到這一點(稍後我將解釋上述堆棧操作過程如何進行批處理)。以下 Python 代碼使用內置於 PyTorch 的文本庫的系統來載入數據,它可以通過連接相似長度的數據樣本自動生成批處理。運行此代碼之後,train_iter、dev_iter 和 test_itercontain 循環遍歷訓練集、驗證集和測試集分塊 SNLI 的批處理。
from torchtext import data, datasets
TEXT = datasets.snli.ParsedTextField(lower=True)
TRANSITIONS = datasets.snli.ShiftReceField()
LABELS = data.Field(sequential=False)train, dev, test = datasets.SNLI.splits(
TEXT, TRANSITIONS, LABELS, wv_type='glove.42B')TEXT.build_vocab(train, dev, test)
train_iter, dev_iter, test_iter = data.BucketIterator.splits(
(train, dev, test), batch_size=64)
你可以在 train.py中找到設置訓練循環和准確性(accuracy)測量的其餘代碼。讓我們繼續。如上所述,SPINN 編碼器包含參數化的 Rece 層和可選的循環跟蹤器來跟蹤句子上下文,以便在每次網路讀取單詞或應用 Rece 時更新隱藏狀態;以下代碼代表的是,創建一個 SPINN 只是意味著創建這兩個子模塊(我們將很快看到它們的代碼),並將它們放在一個容器中以供稍後使用。
import torchfrom torch import nn
# subclass the Mole class from PyTorch』s neural network package
class SPINN(nn.Mole):
def __init__(self, config):
super(SPINN, self).__init__()
self.config = config self.rece = Rece(config.d_hidden, config.d_tracker)
if config.d_tracker is not None:
self.tracker = Tracker(config.d_hidden, config.d_tracker)
當創建模型時,SPINN.__init__ 被調用了一次;它分配和初始化參數,但不執行任何神經網路操作或構建任何類型的計算圖。在每個新的批處理數據上運行的代碼由 SPINN.forward 方法定義,它是用戶實現的方法中用於定義模型向前過程的標准 PyTorch 名稱。上面描述的是堆棧操作演算法的一個有效實現,即在一般 Python 中,在一批緩沖區和堆棧上運行,每一個例子都對應一個緩沖區和堆棧。我使用轉移矩陣(transition)包含的「shift」和「rece」操作集合進行迭代,運行 Tracker(如果存在),並遍歷批處理中的每個樣本來應用「shift」操作(如果請求),或將其添加到需要「rece」操作的樣本列表中。然後在該列表中的所有樣本上運行 Rece 層,並將結果推送回到它們各自的堆棧。
def forward(self, buffers, transitions):
# The input comes in as a single tensor of word embeddings;
# I need it to be a list of stacks, one for each example in
# the batch, that we can pop from independently. The words in
# each example have already been reversed, so that they can
# be read from left to right by popping from the end of each
# list; they have also been prefixed with a null value.
buffers = [list(torch.split(b.squeeze(1), 1, 0))
for b in torch.split(buffers, 1, 1)]
# we also need two null values at the bottom of each stack,
# so we can from the nulls in the input; these nulls
# are all needed so that the tracker can run even if the
# buffer or stack is empty
stacks = [[buf[0], buf[0]] for buf in buffers]
if hasattr(self, 'tracker'):
self.tracker.reset_state()
for trans_batch in transitions:
if hasattr(self, 'tracker'):
# I described the Tracker earlier as taking 4
# arguments (context_t, b, s1, s2), but here I
# provide the stack contents as a single argument
# while storing the context inside the Tracker
# object itself.
tracker_states, _ = self.tracker(buffers, stacks)
else:
tracker_states = itertools.repeat(None)
lefts, rights, trackings = [], [], []
batch = zip(trans_batch, buffers, stacks, tracker_states)
for transition, buf, stack, tracking in batch:
if transition == SHIFT:
stack.append(buf.pop())
elif transition == REDUCE:
rights.append(stack.pop())
lefts.append(stack.pop())
trackings.append(tracking)
if rights:
reced = iter(self.rece(lefts, rights, trackings))
for transition, stack in zip(trans_batch, stacks):
if transition == REDUCE:
stack.append(next(reced))
return [stack.pop() for stack in stacks]
在調用 self.tracker 或 self.rece 時分別運行 Tracker 或 Rece 子模塊的向前方法,該方法需要在樣本列表上應用前向操作。在主函數的向前方法中,在不同的樣本上進行獨立的操作是有意義的,即為批處理中每個樣本提供分離的緩沖區和堆棧,因為所有受益於批處理執行的重度使用數學和需要 GPU 加速的操作都在 Tracker 和 Rece 中進行。為了更干凈地編寫這些函數,我將使用一些 helper(稍後將定義)將這些樣本列表轉化成批處理張量(tensor),反之亦然。
我希望 Rece 模塊自動批處理其參數以加速計算,然後解批處理(unbatch)它們,以便可以單獨推送和彈出。用於將每對左、右子短語表達組合成父短語(parent phrase)的實際組合函數是 TreeLSTM,它是普通循環神經網路單元 LSTM 的變型。該組合函數要求每個子短語的狀態實際上由兩個張量組成,一個隱藏狀態 h 和一個存儲單元(memory cell)狀態 c,而函數是使用在子短語的隱藏狀態操作的兩個線性層(nn.Linear)和將線性層的結果與子短語的存儲單元狀態相結合的非線性組合函數 tree_lstm。在 SPINN 中,這種方式通過添加在 Tracker 的隱藏狀態下運行的第 3 個線性層進行擴展。
圖 2:TreeLSTM 組合函數增加了第 3 個輸入(x,在這種情況下為 Tracker 狀態)。在下面所示的 PyTorch 實現中,5 組的三種線性變換(由藍色、黑色和紅色箭頭的三元組表示)組合為三個 nn.Linear 模塊,而 tree_lstm 函數執行位於框內的所有計算。圖來自 Chen et al. (2016)。
㈢ C語言緩沖區在哪裡
緩沖區具體在哪裡是與操作系統、編譯器相關的
以VC++為例。察看getchar的源代碼(src\fgetchar.c),有:
int __cdecl _fgetchar (void){
return(getc(stdin));
}
#undef getchar
int __cdecl getchar (void){
return _fgetchar();
}
可見getchar()相當於getc(stdin)
繼續察看getc(src\fgetc.c),有一段(為便於閱讀,有刪減):
int __cdecl getc (FILE *stream){
int retval;
_ASSERTE(stream != NULL);
_lock_str(stream);
__try {
retval = _getc_lk(stream);
}
__finally {
_unlock_str(stream);
}
return(retval);
}
這段代碼里_lock_str其實是通過Win32 API提供的臨街區來鎖住文件
接收用戶輸入發生在_getc_lk,_getc_lk宏調用_filbuf。_filbuf在_filbuf.c中可以查看,這段代碼比較長,就不貼出來了
_filbuf主要是調用了_read(_fileno(stream), stream->_base, stream->_bufsiz)
而_read最終則是調用了Win32API ReadFile,以下是用WinDbg輸出的getchar的調用棧:
# ChildEBP RetAddr
00 0012fe6c 0040a4e7 kernel32!ReadFile
01 0012fea8 0040a3b9 TestStruct!_read_lk+0x107 [read.c @ 146]
02 0012fec0 00403140 TestStruct!_read+0x69 [read.c @ 75]
03 0012fee8 00401290 TestStruct!_filbuf+0xd0 [_filbuf.c @ 127]
04 0012ff08 004012cc TestStruct!fgetc+0x80 [fgetc.c @ 44]
05 0012ff14 0040103d TestStruct!getc+0xc [fgetc.c @ 56]
06 0012ff20 00401058 TestStruct!_fgetchar+0xd [fgetchar.c @ 37]
07 0012ff28 0040101e TestStruct!getchar+0x8 [fgetchar.c @ 47]
08 0012ff80 0040115c TestStruct!main+0xe [d:\my programs\teststruct\ts.cpp @ 4]
09 0012ffc0 7c816fe7 TestStruct!mainCRTStartup+0xfc [crt0.c @ 206]
0a 0012fff0 00000000 kernel32!BaseProcessStart+0x23
可見,getchar最終調用了ReadFile。關於ReadFile的原理以及緩沖區在哪裡,請你再提一個問我再回答
㈣ 編譯原理什麼是素短語
編譯原理中,素短語是至少含義一個終結符,並且自身不包含任何更小素短語的一種短語。
素短語是一種特殊的短語,它是一個遞歸的定義,至少含有一個終結符,並且除它自身之外不再含任何更小的素短語,所謂最左素短語就是處於句型最左邊的素短語的短語。
一個算符優先文法G的任何句型的最左素短語是滿足以下條件的最左子串NaNb…NcNdN(N是非終結符,a,b,c,d是終結符)。例如:句型T+T*F+id,T*F是最左素短語,id是素短語。
(4)編譯原理棧緩沖區與句型的關系擴展閱讀:
通過語法樹可以得知素短語:
1、每個句型對應一棵語法樹
2、每棵語法樹的葉子結點從左到右排列構成一個句型
3、每棵語法樹的子樹的葉子結點從左到右排列構成一個短語
4、每棵語法樹的簡單子樹(只有父子兩層結點)的葉子結點從左到右排列構成一個簡單(直接)短語。
5、素短語是至少包含一個終結符的短語,但它不能包含其它素短語。
㈤ 漏洞分析的內容導讀
本書分為5篇,共33章。
第1篇 漏洞利用原理(初級)
第1章 基礎知識
本章著重對漏洞挖掘中的一些基礎知識進行介紹。首先是漏洞研究中的一些基本概念和原理;然後是對Windows平台下可執行文件的結構和內存方面的一些基礎知識的介紹;最後介紹了一些漏洞分析中經常使用的軟體工具。包括調試工具、反匯編工具、二進制編輯工具等。您會在後面的調試實驗中反復見到這些工具的身影。在這章的最後一節,我們設計了一個非常簡單的破解小實驗,用於實踐工具的應用,消除您對二進制的恐懼感,希望能夠給您帶來一些樂趣。
第2章 棧溢出原理與實踐
基於棧的溢出是最基礎的漏洞利用方法。本章首先用大量的示意圖,深入淺出地講述了操作系統中函數調用、系統棧操作等概念和原理;隨後通過三個調試實驗逐步講解如何通過棧溢出,一步一步地劫持進程並植入可執行的機器代碼。即使您沒有任何匯編語言基礎,從未進行過二進制級別的調試,在本章詳細的實驗指導下也能輕松完成實驗,體會到exploit的樂趣。
第3章 開發shellcode的藝術
本章緊接第2章的討論,比較系統地介紹了溢出發生後,如何布置緩沖區、如何定位shellcode、如何編寫和調試shellcode等實際的問題。最後兩小節還給出了一些編寫shellcode的高級技術,供有一定匯編基礎的朋友做參考。
第4章 用MetaSploit開發Exploit
MetaSploit是軟體工程中的Frame Work(架構)在安全技術中的完美實現,它把模塊化、繼承性、封裝等面向對象的特點在漏洞利用程序的開發中發揮得淋漓盡致。使用這個架構開發Exploit要比直接使用C語言寫出的Exploit簡單得多。本章將集中介紹如何使用這個架構進行Exploit開發。
第5章 堆溢出利用
在很長一段時間內,Windows下的堆溢出被認為是不可利用的,然而事實並非如此。本章將用精闢的論述點破堆溢出利用的原理,讓您輕松領會堆溢出的精髓。此外,這章的一系列調試實驗將加深您對概念和原理的理解。用通俗易懂的方式論述復雜的技術是本書始終堅持的原則。
第6章 形形色色的內存攻擊技術
在了解基本的堆棧溢出後,本章將為大家展示更為高級的內存攻擊技術。本章集中介紹了一些曾發表於Black Hat上的著名論文中所提出的高級利用技術,如狙擊Windows異常處理機制、攻擊虛函數、off by one、 Heap Spray等利用技巧。對於安全專家,了解這些技巧和手法不至於在分析漏洞時錯把可以利用的漏洞誤判為低風險類型;對於黑客技術愛好者,這些知識很可能成為激發技術靈感的火花。
第7章 手機里的緩沖區溢出
在PC機上的溢出攻擊進行的如火如荼的時候,您是否也想了解手機平台上的緩沖區溢出問題?那就不要錯過本章!本章以ARM和Windows Mobile為例,介紹手機平台上編程和調試技巧。並在最後以一個手機上的exploit me為大家揭開手機里緩沖區溢出的神秘面紗。
第8章 其他類型的軟體漏洞
緩沖區溢出漏洞只是軟體漏洞的一個方面,我們來看看其他一些流行的安全漏洞。如格式化串漏洞、SQL注入、XPath注入、XSS等安全漏洞產生的原因、利用技巧及防範措施。
第2篇 漏洞利用原理(高級)
第9章 Windows安全機制概述
微軟在Windows XP SP2和Windows 2003之後,向操作系統中加入了許多安全機制。本章將集中討論這些安全機制對漏洞利用的影響。
第10章 棧中的守護天使:GS
針對緩沖區溢出時覆蓋函數返回地址這一特徵,微軟在編譯程序時使用了一個很酷的安全編譯選項——GS。本章將對GS編譯選項的原理進行詳細介紹,並介紹幾種繞過GS的溢出技巧。
第11章 亡羊補牢:SafeSEH
攻擊S.E.H已經成為windows平台下漏洞利用的經典手法。為了遏制日益瘋狂的攻擊,微軟在Windows XP SP2及後續版本的操作系統中引入了著名的S.E.H校驗機制SafeSEH。本章將會對這一安全機制進行詳細的分析,並介紹其中的不足和繞過方法。
第12章 數據與程序的分水嶺:DEP
溢出攻擊的根源在於現代計算機對數據和代碼沒有明確區分這一先天缺陷, 而DEP這種看似釜底抽薪式的防護措施是否真的可以杜絕溢出攻擊呢?答案馬上揭曉。
第13章 在內存中躲貓貓:ASLR
程序載入時不再使用固定的基址載入,ASLR技術將溢出時使用的跳板在內存中隱藏了起來,沒有了跳板我們如何溢出呢?本章將帶領您在黑暗中尋找溢出的出口。
第14章 S.E.H終極防護:SEHOP
SafeSEH的敗北,讓微軟推出一種更為嚴厲的S.E.H保護機制SEHOP。這里將為您展示這種保護機制的犀利之處。
第15章 重重保護下的堆
當堆溢出變成可能後,微軟不能再無視堆中的保護機制了,讓我們一覽堆中的保護機制,並分析其漏洞。
第3篇 漏洞挖掘技術
第16章 漏洞挖掘技術簡介
不論從工程上講還是從學術上講,漏洞挖掘都是一個相當前沿的領域。本章將從動態測試和靜態審計兩方面對漏洞挖掘技術的基礎知識進行簡單的介紹。
第17章 文件類型漏洞挖掘與Smart Fuzz
文件類型的漏洞層出不窮,持續威脅著互聯網的安全。如何系統的測試文件格式,產生精確有效的畸形測試用例用以發掘文件解析器的安全漏洞,並不是一件容易的事情。本章將從理論和實踐兩個方面向您講述灰盒測試技術。
第18章 FTP的漏洞挖掘
本章將簡述FTP協議,並手把手地帶領您完成幾個初級的漏洞測試案例,讓您親身體會下真實的漏洞長什麼模樣。
第19章 E-mail的漏洞挖掘
E-mail系統涉及的安全問題不光只有緩沖區溢出,在本章的挖掘案例中,您會發現除了工具和常用方法外,威力最為強大的武器還是您的大腦。Evil thinking是安全測試中最重要的思維方式之一。
第20章 ActiveX控制項的漏洞挖掘
控制項類漏洞曾經是大量網馬的棲身之地。本章將結合若干個曾經的0 day向您比較系統的介紹這類漏洞的測試、調試的相關工具和方法。
第4篇 操作系統內核安全
第21章 探索ring0
研究內核漏洞,需要首先掌握一些內核基礎知識,例如內核驅動程序的開發、編譯、運行和調試,內核中重要的數據結構等,本章將為讀者開啟探索ring0之門,逐步掌握一些內核基礎知識。
第22章 內核漏洞利用技術
本章將帶領讀者從一個簡單的內核漏洞程序exploitme.sys的編寫開始,展示內核漏洞利用的思路、方法,以及利用程序和Ring0 Shellcode的編寫和設計。
第23章 FUZZ驅動程序
掌握了內核漏洞的原理和利用方法,本章將進入內核漏洞挖掘階段,學習較為高級的內核漏洞挖掘技術,最後實踐該漏洞挖掘技術,分析挖掘出內核漏洞。
第24章 內核漏洞案例分析
本章對幾種典型的內核漏洞,用幾個真實的內核漏洞案例來詳細分析,分析漏洞造成的具體原因和細節,並構造漏洞成功利用的方法。
第5篇 漏洞分析案例
第25章 漏洞分析技術概述
本章縱覽了漏洞分析與調試的思路,並介紹了一些輔助漏洞調試分析的高級逆向工具。
第26章 RPC入侵:MS06-040 與MS08-067
由於可以做到主動式遠程入侵,RPC級別的漏洞被譽為漏洞中的王者,此類漏洞也極其稀有,每一個都有一段曲折的故事。值得一提的是最近的兩個RPC系統漏洞竟然出自同一個函數。本章將對這個縫來補去沒有修好的函數進行詳細分析,讓您從攻防兩方面深刻理解漏洞的起因和修復策略的重要性。
第27章 MS06-055分析:實戰Heap Spray
通過網頁「掛馬」是近年來攻擊者慣用的手法。本章通過分析微軟IE瀏覽器中真實的緩沖區溢出漏洞,告訴您為什麼不能隨便點擊來歷不明的URL鏈接,並在實戰中為大家演示Heap Spray技術。
第28章 MS09-032分析:一個「&」引發的血案
一個視頻網頁的背後可能是一隻兇狠的木馬,這就是著名的Microsoft DirectShow MPEG-2視頻ActiveX控制項遠程代碼執行漏洞。本章將為您分析該漏洞產生的原因及分析技巧。
第29章 Yahoo!Messenger棧溢出漏洞
在波濤洶涌的溢出大潮中Yahoo也沒能倖免,作為國外非常流行的Yahoo!Messenger也存在過非常嚴重的漏洞。本章將重現當時的場景,並分析漏洞產生的原因。
第30章 CVE-2009-0927:PDF中的JS
您可能不會隨便運行一個可執行文件,但是您會想到別人發過來的PDF文檔中也有可能隱藏著一些東西嗎?本章將以PDF文檔為例,帶您領略文件類型溢出漏洞的風采。
第31章 壩之蟻穴:超長URL溢出漏洞
安全軟體不一定安全,即便是這款保護未成年人健康上網的計算機終端過濾軟體,也有可能成為黑客攻擊的窗口。本章將介紹綠壩軟體中一個已經被修復了的安全漏洞。
第32章 暴風影音M3U文件解析漏洞
晚上回家後用暴風影音打開別人發過來的M3U列表文件,在你陶醉於其內容之時,一隻精乾的小馬已悄然在後台運行。想要了解這只小馬是如何進入你的電腦的?請閱讀本章。
第33章 LNK快捷方式文件漏洞
是否我不去運行任何可疑文件,不去打開陌生的網址就安全了呢?答案是否定。LNK快捷方式漏洞無需打開文件,只要瀏覽惡意文件,所在文件夾就會中毒,俗稱「看一眼就掛」。本章將帶您分析這一神奇的漏洞。
Failwest