1. 程式人生 > >ZIMG -- 高效能圖片伺服器淺談

ZIMG -- 高效能圖片伺服器淺談

(偶然的在網上看到這篇文章, 覺得ZIMG很優秀, 只是目前版本是1.0還不支援分散式, 也期望後續版本如作者所說會支援叢集.

專案程式碼可以去github: https://github.com/buaazp/zimg/)

綜述

2011年李彥巨集在百度聯盟峰會上就提到過網際網路的讀圖時代已經到來1,圖片服務早已成為一個網際網路應用中佔比很大的部分,對圖片的處理能力也相應地變成企業和開發者的一項基本技能。需要處理海量圖片的典型應用有:
1. 圖片類應用,如百度相簿。
2. 導購類應用,如Guang.com。
3. 電商類應用,如淘寶。
4. 雲端儲存服務,如七牛雲端儲存。
除此之外幾乎所有的網站都需要考慮自己圖片處理的解決方案,以免在流量變大之後顯得手足無措。
本文將從作者自己設計完成的圖片服務程式

zimg的設計思路出發,探討高效能圖片伺服器的特點、難點和應對辦法。

主要問題

要想處理好圖片,需要面對的三個主要問題是:大流量,高併發,海量儲存。下面將逐一進行討論。

大流量

除了那些擁有自己資料中心的大型企業,中小型企業都需要考慮到流量問題,因為流量就是成本,圖片相對於文字來說流量增加了一個數量級,省下的每一個位元組都是白花花的銀子。我曾經在一篇部落格2裡看到,作者在業務邏輯中引入PHP的imagick模組進行壓縮,短短几行程式碼就做到了每個月為公司節省2萬人民幣的效果,可見凡是涉及到圖片的網際網路應用,都應該統籌規劃,降低流量節約開支。

高併發

高併發的問題在使用者量較低時幾乎不會出現,但是一旦使用者攀升,或者遇到熱點事件,比如淘寶的雙十一,或者網站被人上傳了一張爆炸性的新聞圖片,短時間內將會湧入大量的瀏覽請求,如果架構設計得不好,又沒有緊急應對方案,很可能導致大量的等待、更多的頁面重新整理和更多請求的死迴圈。總的來說,就是要把圖片服務的效能做得足夠好。

海量儲存

在2012年的介紹Facebook圖片儲存的文章3裡提到,當時Facebook使用者上傳圖片15億張,總容量超過了1.5PB,這樣的數量級是一般企業無法承受的。雖然我們很難做出一個可以跟Facebook比肩的應用,但是從架構設計的角度來說,良好的拓展方案還是要有的。我們需要提前設計出最合適的海量圖片資料儲存方案和操作方便的拓容方案,以應對將來不斷增長的業務需求。

以上三個問題,其實也是相互制約和鉗制的,比如要想降低流量,就需要大量的計算,導致請求處理時間延長,系統單位時間內的處理能力下降;再比如為了儲存更多的圖片,必然要在查詢上消耗資源,同樣也會降低處理能力。所以,圖片服務雖然看起來業務簡單,實際做起來也不是一件小事。

設計方案

zimg是作者針對圖片處理伺服器而設計開發的開源程式,它擁有很高的效能,也滿足了應用在圖片方面最基本的處理需求,下面將從架構設計、程式碼邏輯和效能測試等方面進行介紹。

總體思路

想要在展現圖片這件事情上有最好的表現,首先需要從整體業務中將圖片服務部分分離出來。使用單獨的域名和建立獨立的圖片伺服器有很多好處,比如:
1. CDN分流。如果你有注意的話,熱門網站的圖片地址都有特殊的域名,比如微博的是ww1.sinaimg.cn,人人的是fmn.xnpic.com等等,域名不同可以在CDN解析的層面就做到非常明顯的優化效果。
2. 瀏覽器併發連線數限制。一般來說,瀏覽器載入HTML資源時會建立很多的連線,並行地下載資源。不同的瀏覽器對同一主機的併發連線數限制是不同的,比如IE8是10個,Firefox是30個。如果把圖片伺服器獨立出來,就不會佔用掉對主站連線數的名額,一定程度上提升了網站的效能。
3. 瀏覽器快取。現在的瀏覽器都具有快取功能,但是由於cookie的存在,大部分瀏覽器不會快取帶有cookie的請求,導致的結果是大量的圖片請求無法命中,只能重新下載。獨立域名的圖片伺服器,可以很大程度上緩解此問題。

圖片伺服器被獨立出來之後,會面臨兩個選擇,主流的方案是前端採用Nginx,中間是PHP或者自己開發的模組,後端是物理儲存;比較特別一些的,比如Facebook,他們把圖片的請求處理和儲存合併成一體,叫做haystack,這樣做的好處是,haystack只會處理與圖片相關的請求,剝離了普通http伺服器繁雜的功能,更加輕量高效,同時也使部署和運維難度降低。
zimg採用的是與Facebook相似的策略,將圖片處理的大權收歸自己所有,絕大部分事情都由自己處理,除非特別必要,最小程度地引入第三方模組。
注:zimg的1.0版本,設計面向圖片量在TB級別的中小型服務,物理儲存暫時不支援分散式叢集,分散式功能將在2.0版本中完成。

架構設計

為了極致的效能表現,zimg全部採用C語言開發,總體上分為三個層次,前端http處理層,中間圖片處理層和後端的儲存層。下圖為zimg架構設計圖:
總體架構

http處理層引入基於libevent的libevhtp庫,libevhtp是一款專門處理基本http請求的庫,它太適合zimg的業務場景了,在效能和功能之間找到了很好的平衡點。圖片處理層採用imagemagick庫,imagemagick是現在公認功能最強,效能最好的圖片處理函式庫。儲存層採用memcached快取加直接讀寫硬碟的方案,更加深入的優化將在後續進行,比如引入TFS4等。為了避免資料庫帶來的效能瓶頸,zimg不引入結構化資料庫,圖片的查詢全部採用雜湊來解決。
事實上圖片伺服器的設計,是一個在I/O與CPU運算之間的博弈過程,最好的策略當然是繼續拆:CPU敏感的http和圖片處理層部署於運算能力更強的機器上,記憶體敏感的cache層部署於記憶體更大的機器上,I/O敏感的物理儲存層則放在配備SSD的機器上,但並不是所有人都能負擔得起這麼奢侈的配置。zimg折中成本和業務需求,目前只需要部署在一臺伺服器上。由於不同伺服器硬體不同,I/O和CPU運算速度差異很大,很難一棒子定死。zimg所選擇的思路是,儘量減少I/O,將壓力放在CPU上,事實證明這樣的思路基本沒錯,在硬碟效能很差的機器上效果更加明顯;即使以後SSD全面普及,CPU的運算能力也會相應提升,總體來說zimg的方案也不會太失衡。

程式碼層面

雖然zimg在二進位制實體上沒有分模組,上面已經提到了原因,現階段面向中小型的服務,單機部署即可,但是程式碼上是分離的,下面介紹主要部分的功能和實現,更詳細的內容可以從github上拉下來研究。熱烈歡迎大家fork和contribute。

main.c是程式的入口,主要功能是處理啟動引數,部分引數功能如下:

-p [port] 監聽埠號,預設4869
-t [thread_num] 執行緒數,預設4,請調整為具體伺服器的CPU核心數
-k [max_keepalive_num] 最高保持連線數,預設1,不啟用長連線,0為啟用
-l 啟用log,會帶來很大的效能損失,自行斟酌是否開啟
-M [memcached_ip] 啟用快取的連線IP
-m [memcached_port] 啟用快取的連線埠
-b [backlog_num] 每個執行緒的最大連線數,預設1024,酌情設定

zhttpd.c是解析http請求的部分,分為GET和POST兩大部分,GET請求會根據請求的URL引數去尋找圖片並轉給圖片處理層處理,最後將結果返回給使用者;POST接收上傳請求然後將圖片存入計算好的路徑中。
為了實現zimg的總體設計願景,zhttpd承擔了很大部分的工作,也有一些關鍵點,下面撿重點的說一下:

在zimg中圖片的唯一Key值就是該圖片的MD5,這樣既可以隱藏路徑,又能減少前端(指zimg前面的部分,可能是你的應用伺服器)和zimg本身的儲存壓力,是避免引入結構化儲存部分的關鍵,所以所有GET請求都是基於MD5拼接而成的。
大家設想一下,假如你的網站某個地方需要展示一張圖片,這個圖片原圖的大小是1000*1000,但是你想要展示的地方只有300*300,你會怎麼做呢?一般還是依靠CSS來進行控制,但是這樣的話就會造成很多流量的浪費。為此,zimg提供了圖片裁剪功能,你所需要做的就是在圖片URL後面加上w=300&h=300(width和height)即可。
另一個情景是圖片灰白化,比如某天遇到重大自然災害,想要網站所有圖片變成灰白的,那麼只需在圖片URL後面再加上g=1(gray)即可。
當然,依託於imagemagick所提供的完善的圖片處理函式,zimg將在後續版本中逐步增加功能,比如加水印等。

在圖片上傳部分,其實能玩的花樣很少,但是編寫程式碼所消耗的時間最多。現在我們再假設一種情景,如果我們的圖片伺服器前端採用Nginx,上傳功能用PHP實現,需要寫的程式碼很少,但是效能如何呢,答案是很差。首先PHP接收到Nginx傳過來的請求後,會根據http協議(RFC1867)分離出其中的二進位制檔案,儲存在一個臨時目錄裡,等我們在PHP程式碼裡使用$_FILES["upfile"][tmp_name]獲取到檔案後計算MD5再儲存到指定目錄,在這個過程中有一次讀檔案一次寫檔案是多餘的,其實最好的情況是我們拿到http請求中的二進位制檔案(最好在記憶體裡),直接計算MD5然後儲存。
於是我去閱讀了PHP的原始碼,自己實現了POST檔案的解析,讓http層直接和儲存層連在了一起,提高了上傳圖片的效能。關於RFC1867的內容和PHP是如何處理的,感興趣的讀者可以去搜索瞭解下,這裡推薦@Laruence的文章《PHP檔案上傳原始碼分析(RFC1867) 》
除了POST請求這個例子,zimg程式碼中有多處都體現了這種“減少磁碟I/O,儘量在記憶體中讀寫”和“避免記憶體複製”的思想,一點點的積累,最終將會帶來優秀的表現。

zimg.c是呼叫imagemagick處理圖片的部分,這裡先解釋一下在zimg中圖片儲存路徑的規劃方案。
上文曾經提到,現階段zimg服務於儲存量在TB級別的單機圖片伺服器,所以儲存路徑採用2級子目錄的方案。由於Linux同目錄下的子目錄數最好不要超過2000個,再加上MD5的值本身就是32位十六進位制數,zimg就採取了一種非常取巧的方式:根據MD5的前六位進行雜湊,1-3位轉換為十六進位制數後除以4,範圍正好落在1024以內,以這個數作為第一級子目錄;4-6位同樣處理,作為第二級子目錄;二級子目錄下是以MD5命名的資料夾,每個MD5資料夾記憶體儲圖片的原圖和其他根據需要儲存的版本,假設一個圖片平均佔用空間200KB,一臺zimg伺服器支援的總容量就可以計算出來了:

1024 * 1024 * 1024 * 200KB = 200TB

這樣的數量應該已經算很大了,在200TB的範圍內可以採用加硬碟的方式來拓容,當然如果有更大的需求,請期待zimg後續版本的分散式叢集儲存支援。
除了路徑規劃,zimg另一大功能就是壓縮圖片。從使用者角度來說,zimg返回來的圖片只要看起來跟原圖差不多就行了,如果確實需要原圖,也可以通過將所有引數置空的方式來獲得。基於這樣的條件,zimg.c對於所有轉換的圖片都進行了壓縮,壓縮之後肉眼幾乎無法分辨,但是體積將減少67.05%。具體的處理方式為:

圖片裁剪時使用LanczosFilter濾鏡;
以75%的壓縮率進行壓縮;
去除圖片的Exif資訊;
轉換為JPEG格式。

經過這樣的處理之後可以很大程度的減少流量,實現設計目標。

zcache.c是引入memcached快取的部分,引入快取是很重要的,尤其是圖片量級上升之後。在zimg中快取被作為一個很重要的功能,幾乎所有zimg.c中的查詢部分都會先去檢查快取是否存在。比如:
我想要a(代表某MD5)圖片裁剪為100*100之後再灰白化的版本,那麼過程是先去找a&w=100&h=100&g=1的快取是否存在,不存在的話去找這個檔案是否存在(這個請求所對應的檔名為 a/100*100pg),還不存在就去找這個解析度的彩色圖快取是否存在,若依然不存在就去找彩色圖檔案是否存在(對應的檔名為 a/100*100p),若還是沒有,那就去查詢原圖的緩,原圖快取依然未命中的話,只能開啟原圖檔案了,然後開始裁剪,灰白化,然後返回給使用者並存入快取中。
可以看出,上面過程中如果某個環節命中快取,就會相應地減少I/O或圖片處理的運算次數。眾所周知記憶體和硬碟的讀寫速度差距是巨大的,那麼這樣的設計對於熱點圖片抗壓將會十分重要。

除了上述核心程式碼以外就是一些支援性的程式碼了,比如log部分,md5計算部分,util部分等。

效能測試

為了橫向對比zimg的效能,我用PHP寫了一個功能一模一樣的後端,僅用時一下午,這充分證明了“PHP是世界上最好的語言”,也同時說明了用C語言來進行開發是多麼的辛苦,不過,我喜歡效能測試結果出來之後的那份成就感,這樣的付出我覺得是值得的。

測試方案

採用Apache自帶的測試程式ab對指定請求進行測試,在特定併發數100的情況下進行10w個請求的測試,結果依據該併發下每秒處理請求數來定性,對比的方案是未啟用快取的zimg,啟用快取的zimg和Nginx+PHP,其中zimg埠為4868,Nginx埠為80。

測試命令分別為:

ab2 -c 100 -n 100000 http://127.0.0.1:4869/5f189d8ec57f5a5a0d3dcba47fa797e2
ab2 -c 100 -n 100000 http://127.0.0.1:80/zimg.php?md5=5f189d8ec57f5a5a0d3dcba47fa797e2
ab2 -c 100 -n 100000 http://127.0.0.1:4869/5f189d8ec57f5a5a0d3dcba47fa797e2?w=100&h=100&g=1
ab2 -c 100 -n 100000 http://127.0.0.1:80/zimg.php?md5=5f189d8ec57f5a5a0d3dcba47fa797e2&w=100&h=100&g=1

注:以下測試資料單位皆為rps(request per second)。

測試環境

作業系統:openSUSE 12.3
CPU:Intel Xeon E3-1230 V2
記憶體:8GB DDR3 1333MHz
硬碟:西部資料 1TB 7200轉

軟體版本

zimg:1.0.0
Nginx:1.2.9
PHP:5.3.17

測試結果

測試專案 zimg zimg+memcached Nginx+PHP
靜態圖片 2857.80 4995.95 426.56
動態裁剪圖片 2799.34 4658.35 58.61

總的來說測試結果符合預期,純C寫成並且專門為圖片而做了大量優化的zimg表現遠遠優於採用PHP的方案,效能有6-79倍的提升。

高壓測試

在測試過程中由於php-fpm的效能瓶頸,導致併發壓力根本壓不上去,為了充分展現zimg面對超高併發的抗壓能力,我又做了另一項對比測試,即單純的echo測試。測試方法是在逐漸升高的併發壓力下完成20w個echo請求,記錄每種併發壓力下的處理能力。硬體環境不變,這次所要對比的是業界以效能著稱的Nginx,Nginx和zimg都是接收echo請求後返回簡單的“It works!”頁面,不做任何複雜的業務。

測試命令分別為:

ab2 -c 5000 -n 200000 http://127.0.0.1:4869/
ab2 -c 5000 -n 200000 http://127.0.0.1:80/

測試結果如下:

Concurrency zimg Nginx
100 32765.35 33412.12
300 32991.86 32063.05
500 31364.29 30599.07
1000 28936.67 28163.63
2000 27939.02 25124.51
3000 28168.56 22053.22
4000 28463.45 21464.88
5000 27947.37 13536.93
6000 27533.83 14430.21
7000 27502.03 14623.62
8000 26505.07 13389.28
9000 27124.89 13650.01
10000 27446.23 10901.13
11000 26335.22 10585.73
12000 27068.68 10461.54
13000 26798.55 8530.11
14000 26741.93 7628.09
15000 26556.54 9832.16
16000 26815.70 8018.44
17000 27811.33 7951.21
18000 25722.97 6246.00
19000 26730.02 8134.93
20000 27678.67 6106.95

這是一份有趣的資料,其實測試過程中,Nginx在併發1000開始已經出現了部分失敗,在併發9000以後就無法完成20w個請求,通過不斷降低請求數才勉強完成了測試。而強大的zimg毫無壓力地完成了20000併發以內的所有測試,沒有一個失敗返回。為了直觀地顯示測試結果請參考下圖:

測試結果

由於去掉了不需要的複雜功能,zimg在http處理層面要遠比Nginx輕量,同時測試資料也說明了它的高併發抗壓能力。能有這樣的成績則完全要歸功於libevhtp專案,它比libevent自帶的http庫要優秀得多。在我設計zimg的早期版本時,選用了libevent自帶的evhttp庫,然後採用執行緒池的方式來實現多執行緒處理,結果發現在高壓力之下問題頻出,最後無奈放棄。該版本封存在github上的zimg_workqueue分支中,也算是一個紀念吧。

最後

圖片伺服器的設計方案多種多樣,zimg也只是提供了其中的一種思路而已,它才剛剛誕生,以後還有很長的路要走,共同學習,共同進步。
在孤獨而漫長的開發過程中,經常會遇到思維枯竭毫無頭緒的時候,感謝好基友@Xscape給予大量建設性指導性意見;還有@喀啦喀拉在儲存路徑規劃問題上提供的思路。
那麼就用這樣一句經典而充滿力量的話作為結尾吧。

We stand on the shoulders of giants.


相關推薦

ZIMG -- 高效能圖片伺服器

(偶然的在網上看到這篇文章, 覺得ZIMG很優秀, 只是目前版本是1.0還不支援分散式, 也期望後續版本如作者所說會支援叢集. 專案程式碼可以去github: https://github.com/buaazp/zimg/) 綜述 2011年李彥巨集在百度聯盟峰會上

如何使用Netty開發高效能的RPC伺服器

如何使用Netty進行RPC伺服器的開發,技術原理涉及如下:1、定義RPC請求訊息、應答訊息結構,裡面要包括RPC的介面定義模組,如遠端呼叫的類名、方法名、引數結構、引數值等資訊。 2、服務端初始化的時候通過容器載入RPC介面定義和RPC介面實現類物件的對映關係,然後等待客戶端發起呼叫請求。 3、客戶端發

圖片伺服器的架構演進

現在幾乎任何一個網站、Web App以及移動APP等應用都需要有圖片展示的功能,對於圖片功能從下至上都是很重要的。必須要具有前瞻性的規劃好圖片伺服器,圖片的上傳和下載速度至關重要,當然這並不是說一上來就搞很NB的架構,至少具備一定擴充套件性和穩定性。雖然各種架構設計都有,

js本地圖片預覽

.get url 添加圖片 doc let 圖片上傳 獲取 gin radi   最近在工作中遇到一個問題,就是實現一個反饋頁面,這個反饋頁面的元素有反饋主題、反饋類型、反饋內容、反饋人聯系電話以及反饋圖片。前端將這些反饋的元素POST給後臺提供的接口;實現這個工作的步驟就

轉——如何提高伺服器併發處理能力

目錄   (一)什麼是伺服器併發處理能力 (二)有什麼方法衡量伺服器併發處理能力 1.吞吐率 2.壓力測試 (三)怎麼提高伺服器的併發處理能力 1,提高CPU併發計算能力(1)多程序&多執行緒(2)減少程序切換,使用執行緒,考慮程序繫結CPU(3)減少使用不必要的鎖,考慮無鎖程

個人部落格網站or屌絲vps伺服器暴露真實IP的危險性

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

萬樹IT:ASP.NET和Web伺服器

如何使用Silverlight 2的Beta1版本建造一個簡單的Digg客戶端應用。教程旨在按順序閱讀,幫著解釋Silverlight的一些核心程式設計概念。 使用 VS 2008 建立一個新的Silverlight 應用我們來開始我們的Digg應用的開發,先選擇Visual Studio 20

高效能併發應用的架構設計

國人人口基數註定了面向2C架構設計逃不開高效能併發的需求,作為技術負責人(架構師)在這方面的考量是決定應用成功運營的關鍵因素。高效能併發的需求根源在於資料/資料的相關關聯性和集中性,這裡所描述的資源/資料包括但不侷限於: 寬頻資源 靜態資源(圖片、Html、CSS等) 資料庫靜

|WEB 伺服器 -- Caddy

淺談|WEB 伺服器 -- Caddy 2018年03月28日 12:38:00 yori_chen 閱讀數:1490 標籤: caddyserverwebhttps反向代理 更多

開發高效能的MongoDB應用—MongoDB效能優化

效能與使用者量     “如何能讓軟體擁有更高的效能?”,我想這是一個大部分開發者都思考過的問題。效能往往決定了一個軟體的質量,如果你開發的是一個網際網路產品,那麼你的產品效能將更加受到考驗,因為你面對的是廣大的網際網路使用者,他們可不是那麼有耐心的。嚴重點說,頁面的載入速度每增加

Android-Glide的原始碼-優雅的圖片載入框架(一)

摘要 網上GLide的原始碼分析文章太多了,以至於我自己看的時候都不知道從哪看,最近正好有時間整理一下文件。首先要肯定的一點是,我必須承認,Glide原始碼我沒讀完,相比較volley等框架的原始碼,Glide的原始碼在我看來更是一個極其複雜的設計,雖然它的使用時很簡單,但

高效能資料庫叢集 —— 讀寫分離

最近學習了阿里資深技術專家李運華的架構設計關於讀寫分離的教程,頗有收穫,總結一下。 本文主要介紹高效能資料庫叢集讀寫分離相關理論,基本架構,涉及的複雜度問題以及常見解決方案。 1. 讀寫分離概述 讀寫分離概述 基本架構圖: 2.

Facebook的伺服器架構

 大體層次劃分   Facebook的架構可以從不同角度來換分層次。   一種是:   一邊是PHP整的經典的LAMP stack;另外一邊是非PHP整的各種service。     Facebook的頁面從剛創立的時候扎克伯格寫的,到現在,都用PHP開發。後端

伺服器架構之爆服頁遊

首先這裡說的“爆服”頁遊,也是根據當下的頁遊情況來說的,也就是大量開新服的模式。 這裡就涉及到一個問題,為什麼現在頁遊大多是這種模式呢? 本人經歷過一個實際的遊戲專案,當時是在騰訊空間上線的一款遊戲

伺服器架構之MMORPG端遊

MMORPG,是英文Massive(或Massively)Multiplayer Online Role-PlayingGame的縮寫。一般指大型多人線上角色扮演遊戲。 這裡突出的需求就是多人線上,

facebook伺服器架構

大體層次劃分 Facebook的架構可以從不同角度來換分層次。 一種是:一邊是PHP整的經典的LAMP stack;另外一個是非PHP整的各種service。 Facebook的頁面從剛創立的時候扎克伯格寫的,到現在,都用PHP開發。後端有用各種語言開發的service。它們之間用跨語言的thrift

B/S客戶端與伺服器端互動資料(一)

淺談Web資料互動(一)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 追風 C/S模式和B/S各有其有點也各有其缺點,B/S模式在開發中需要考慮資料如何從客

android中載入高清大圖及圖片壓縮方式(二)

  這一講就是本系列的第二篇,一起來聊下關於android中載入高清大圖的問題,我們都知道如果我們直接載入原圖的話,一個是非常慢,需要等待一定時間,如果沒有在一定的時間內給使用者響應的話,將會極大影響使用者的體驗。另一個是如果你的手機記憶體小的話,可能會直接崩潰。這也就是直

阿里雲伺服器哪個地域好?從使用者體驗的角度如何對其進行選擇

阿里雲伺服器最近在國內非常流行,購買的人和企業也是數不勝數,但很多人在選購的時候其實有很多疑問,譬如說,到底該選擇哪個地域?接下來我們複製一篇A5的文章來為大家做個解答: 由於我從11年就開始用阿里雲的伺服器,雖然是個人站長但比較愛玩站。現在手裡阿里雲的伺服器也有5個,各個地域的基本上都有用過

]幾種伺服器端模型——多執行緒併發式(執行緒池)

 (如果不加以說明,我們都是考慮開發是基於GNU/Linux的)在Linux下建立一個執行緒的方式很簡單,pthread_create() 函式來建立執行緒,其中的一個引數的回撥函式,也就是執行緒本身的執行體函式。 ? 1 void *thread_e