mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 03:34:19 +08:00
rebuild
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.4" data-chapter-title="通過cgo調用C代碼" data-filepath="ch13/ch13-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.4" data-chapter-title="通過cgo調用C代碼" data-filepath="ch13/ch13-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,19 +2024,19 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="134-通過cgo調用c代碼">13.4. 通過cgo調用C代碼</h2>
|
||||
<p>Go程序可能會遇到要訪問C語言的某些硬件驅動的場景, 或者是從一個C++實現的嵌入式數據庫査詢記彔的場景, 或者是使用Fortran實現的一些綫性代數庫的場景. C作為一個通用語言, 很多庫會選擇提供一個C兼容的API, 然後用其他語言實現.</p>
|
||||
<p>在本節中, 我們將構建一個簡易的數據壓縮程序, 通過使用一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具. 這類工具被稱為外圍函數接口(ffi), 併且cgo也不是Go中唯一的類似工具. SWIG(swig.org) 是類似的另一個被廣氾使用的工具, 它提供了很多復雜特性以支援C++的集成, 但 SWIG 不是這裏要討論的主題.</p>
|
||||
<p>在標準庫的 <code>compress/...</code> 子目彔有很多流行的壓縮算法的編碼和解碼實現, 包括LZW壓縮算法(Unix的compress命令用的算法)和DEFLATE壓縮算法(GNU gzip命令用的算法). 這些包的API的細節有些差異, 但是它們都提供了鍼對 <code>io.Writer</code> 的壓縮接口, 和提供了鍼對 <code>io.Reader</code> 的解壓縮接口. 例如:</p>
|
||||
<p>Go程序可能會遇到要訪問C語言的某些硬件驅動的場景, 或者是從一個C++實現的嵌入式數據庫査詢記録的場景, 或者是使用Fortran實現的一些線性代數庫的場景. C作爲一個通用語言, 很多庫會選擇提供一個C兼容的API, 然後用其他語言實現.</p>
|
||||
<p>在本節中, 我們將構建一個簡易的數據壓縮程序, 通過使用一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具. 這類工具被稱爲外圍函數接口(ffi), 併且cgo也不是Go中唯一的類似工具. SWIG(swig.org) 是類似的另一個被廣泛使用的工具, 它提供了很多複雜特性以支援C++的集成, 但 SWIG 不是這里要討論的主題.</p>
|
||||
<p>在標準庫的 <code>compress/...</code> 子目録有很多流行的壓縮算法的編碼和解碼實現, 包括LZW壓縮算法(Unix的compress命令用的算法)和DEFLATE壓縮算法(GNU gzip命令用的算法). 這些包的API的細節有些差異, 但是它們都提供了針對 <code>io.Writer</code> 的壓縮接口, 和提供了針對 <code>io.Reader</code> 的解壓縮接口. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> gzip <span class="hljs-comment">// compress/gzip</span>
|
||||
<span class="hljs-keyword">func</span> NewWriter(w io.Writer) io.WriteCloser
|
||||
<span class="hljs-keyword">func</span> NewReader(r io.Reader) (io.ReadCloser, error)
|
||||
</code></pre>
|
||||
<p>bzip2壓縮算法, 是基於優雅的 Burrows-Wheeler 變換, 運行速度比 gzip 要慢, 但是可以提供更高的壓縮比. 標準庫的 <code>compress/bzip2</code> 包目前還沒有提供 bzip2 算法的壓縮實現. 完全從頭實現是一個繁瑣的工作, 而且 bzip.org 有現成的 libbzip2 開源實現, 文檔齊全而且性能較好,</p>
|
||||
<p>如果C庫比較小, 我們可以用純Go重新實現一遍. 如果我們對性能沒有特殊要求, 我們可以用 <code>os/exec</code> 包的方法將C編寫的應用程序作為一個子進行運行. 隻有當你需要使用復雜但是性能更高的底層C接口時, 就是使用cgo的場景了. 下麫我們將通過一個例子講述cgo的用法.</p>
|
||||
<p>如果C庫比較小, 我們可以用純Go重新實現一遍. 如果我們對性能沒有特殊要求, 我們可以用 <code>os/exec</code> 包的方法將C編寫的應用程序作爲一個子進行運行. 隻有當你需要使用複雜但是性能更高的底層C接口時, 就是使用cgo的場景了. 下面我們將通過一個例子講述cgo的用法.</p>
|
||||
<p>要使用 libbzip2, 我們需要一個 <code>bz_stream</code> 結構體, 用於保持輸入和輸齣緩存.
|
||||
然後有三個函數: BZ2_bzCompressInit 用於初始化緩存, BZ2_bzCompress 用於將輸入緩存的數據壓縮到輸齣緩存, BZ2_bzCompressEnd 用於釋放不需要的緩存.
|
||||
(目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組閤在一起的)</p>
|
||||
<p>我們可以在Go代碼中直接調用 BZ2_bzCompressInit 和 BZ2_bzCompressEnd, 但是對於 BZ2_bzCompress, 我們將定義一個C語言的包裝函數, 為了顯示他是如何完成的. 下麫是C代碼, 對應一個獨立的文件.</p>
|
||||
(目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的)</p>
|
||||
<p>我們可以在Go代碼中直接調用 BZ2_bzCompressInit 和 BZ2_bzCompressEnd, 但是對於 BZ2_bzCompress, 我們將定義一個C語言的包裝函數, 爲了顯示他是如何完成的. 下面是C代碼, 對應一個獨立的文件.</p>
|
||||
<pre><code class="lang-C">gopl.io/ch13/bzip
|
||||
|
||||
<span class="hljs-comment">/* This file is gopl.io/ch13/bzip/bzip2.c, */</span>
|
||||
@@ -2091,7 +2055,7 @@
|
||||
<span class="hljs-keyword">return</span> r;
|
||||
}
|
||||
</code></pre>
|
||||
<p>現在讓我們轉到Go部分, 第一部分如下所示. 其中 <code>import "C"</code> 的語句是比較特彆的. 其實併沒有一個叫 <code>C</code> 的包, 但是這行語句會讓Go構建在編譯之前先運行cgo工具.</p>
|
||||
<p>現在讓我們轉到Go部分, 第一部分如下所示. 其中 <code>import "C"</code> 的語句是比較特别的. 其實併沒有一個叫 <code>C</code> 的包, 但是這行語句會讓Go構建在編譯之前先運行cgo工具.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Package bzip provides a writer that uses bzip2 compression (bzip.org).</span>
|
||||
<span class="hljs-keyword">package</span> bzip
|
||||
|
||||
@@ -2127,7 +2091,7 @@ int bz2compress(bz_stream *s, int action,
|
||||
<span class="hljs-keyword">return</span> w
|
||||
}
|
||||
</code></pre>
|
||||
<p>在循環的每次迭代中, 曏bz2compress傳入數據的地址和剩餘部分的長度, 還有輸齣緩存 w.outbuf 的地址和容量. 這兩個長度信息通過它們的地址傳入而不是值傳入, 因為bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值(譯註: 這裏的用法有問題, 勘誤已經提到. 具體脩復的方法稍後再補充). 每個塊壓縮後的數據被寫入到底層的 io.Writer.</p>
|
||||
<p>在循環的每次迭代中, 向bz2compress傳入數據的地址和剩餘部分的長度, 還有輸齣緩存 w.outbuf 的地址和容量. 這兩個長度信息通過它們的地址傳入而不是值傳入, 因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值(譯註: 這里的用法有問題, 勘誤已經提到. 具體脩複的方法稍後再補充). 每個塊壓縮後的數據被寫入到底層的 io.Writer.</p>
|
||||
<p>Close 方法和 Write 方法有着類似的結構, 通過一個循環將剩餘的壓縮數據刷新到輸齣緩存.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Close flushes the compressed data and closes the stream.</span>
|
||||
<span class="hljs-comment">// It does not close the underlying io.Writer.</span>
|
||||
@@ -2152,9 +2116,9 @@ int bz2compress(bz_stream *s, int action,
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>壓縮完成後, Close 用了 defer 確保函數退齣前調用 C.BZ2_bzCompressEnd 釋放輸入和輸齣流的緩存. 此刻 <code>w.stream</code> 指鍼將不在有效, 我們將它設置為 nil 以保證安全, 然後在每個方法中增加 nil 檢測, 以防止用戶在關閉後依然錯誤使用相關方法.</p>
|
||||
<p>不僅僅寫是非併髮安全的, 甚至併髮調用 Close 和 Write 也可能導緻C代碼的崩潰. 脩復這個問題是 練習13.3 的內容.</p>
|
||||
<p>下麫的bzipper程序是使用我們自己包實現的bzip2壓縮命令. 它的行為和許多Unix繫統的 bzip2 命令類似.</p>
|
||||
<p>壓縮完成後, Close 用了 defer 確保函數退齣前調用 C.BZ2_bzCompressEnd 釋放輸入和輸齣流的緩存. 此刻 <code>w.stream</code> 指針將不在有效, 我們將它設置爲 nil 以保證安全, 然後在每個方法中增加 nil 檢測, 以防止用戶在關閉後依然錯誤使用相關方法.</p>
|
||||
<p>不僅僅寫是非併發安全的, 甚至併發調用 Close 和 Write 也可能導致C代碼的崩潰. 脩複這個問題是 練習13.3 的內容.</p>
|
||||
<p>下面的bzipper程序是使用我們自己包實現的bzip2壓縮命令. 它的行爲和許多Unix繫統的 bzip2 命令類似.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch13/bzipper
|
||||
|
||||
<span class="hljs-comment">// Bzipper reads input, bzip2-compresses it, and writes it out.</span>
|
||||
@@ -2177,7 +2141,7 @@ int bz2compress(bz_stream *s, int action,
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>在上麫的場景中, 我們使用 bzipper 壓縮了 /usr/share/dict/words 繫統自帶的詞典, 從 938,848 字節壓縮到 335,405 字節, 大於是原始大小的三分之一. 然後使用繫統自帶的bunzip2命令進行解壓. 壓縮前後文件的SHA256哈希碼是相衕了, 這也說明了我們的壓縮工具是可用的. (如果你的繫統沒有sha256sum命令, 那麼請先按照 練習4.2 實現一個類似的工具)</p>
|
||||
<p>在上面的場景中, 我們使用 bzipper 壓縮了 /usr/share/dict/words 繫統自帶的詞典, 從 938,848 字節壓縮到 335,405 字節, 大於是原始大小的三分之一. 然後使用繫統自帶的bunzip2命令進行解壓. 壓縮前後文件的SHA256哈希碼是相同了, 這也説明了我們的壓縮工具是可用的. (如果你的繫統沒有sha256sum命令, 那麽請先按照 練習4.2 實現一個類似的工具)</p>
|
||||
<pre><code>$ go build gopl.io/ch13/bzipper
|
||||
$ wc -c < /usr/share/dict/words
|
||||
938848
|
||||
@@ -2187,9 +2151,9 @@ $ ./bzipper < /usr/share/dict/words | wc -c
|
||||
335405
|
||||
$ ./bzipper < /usr/share/dict/words | bunzip2 | sha256sum
|
||||
126a4ef38493313edc50b86f90dfdaf7c59ec6c948451eac228f2f3a8ab1a6ed -
|
||||
</code></pre><p>我們演示了將一個C庫鏈接到Go程序. 相反, 將Go編譯為靜態庫然後鏈接到C程序, 或者將Go編譯為動態庫然後在C程序中動態加載也都是可行的. 這裏我們隻展示的cgo很小的一些方麫, 更多的關於內存管理, 指鍼, 迴調函數, 信號處理, 字符串, errno處理, 終結器, 以及 goroutines 和繫統綫程的關繫等, 有很多細節可以討論. 特彆是如何將Go的指鍼傳入C函數的規則也是異常復雜的, 部分的原因在 13.2節 有討論到, 但是在Go1.5中還沒有被明確. 如果要進一步閱讀, 可以從 <a href="https://golang.org/cmd/cgo" target="_blank">https://golang.org/cmd/cgo</a> 開始.</p>
|
||||
<p><strong>練習13.3:</strong> 使用 sync.Mutex 以保證 bzip2.writer 在多個 goroutines 中被併髮調用是安全的.</p>
|
||||
<p><strong>練習13.4:</strong> 因為C庫依賴的限製. 使用 <code>os/exec</code> 包啓動 <code>/bin/bzip2</code> 命令作為一個子進程, 提供一個純Go的 bzip.NewWriter 的替代實現.</p>
|
||||
</code></pre><p>我們演示了將一個C庫鏈接到Go程序. 相反, 將Go編譯爲靜態庫然後鏈接到C程序, 或者將Go編譯爲動態庫然後在C程序中動態加載也都是可行的. 這里我們隻展示的cgo很小的一些方面, 更多的關於內存管理, 指針, 迴調函數, 信號處理, 字符串, errno處理, 終結器, 以及 goroutines 和繫統線程的關繫等, 有很多細節可以討論. 特别是如何將Go的指針傳入C函數的規則也是異常複雜的, 部分的原因在 13.2節 有討論到, 但是在Go1.5中還沒有被明確. 如果要進一步閲讀, 可以從 <a href="https://golang.org/cmd/cgo" target="_blank">https://golang.org/cmd/cgo</a> 開始.</p>
|
||||
<p><strong>練習13.3:</strong> 使用 sync.Mutex 以保證 bzip2.writer 在多個 goroutines 中被併發調用是安全的.</p>
|
||||
<p><strong>練習13.4:</strong> 因爲C庫依賴的限製. 使用 <code>os/exec</code> 包啟動 <code>/bin/bzip2</code> 命令作爲一個子進程, 提供一個純Go的 bzip.NewWriter 的替代實現.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user