阿里二面面經

1.windows/linux,多線程/多進程

IBM測試,切換線程context的時候,windows比linux快一倍多。進出最快的鎖(windows2k的 critical section和linux的pthread_mutex),windows比linux的要快五倍左右。可見多線程這個具體的領域內,linux仍是稍遜windows一點。這應該是情有可原的,畢竟unix家族都是從多進程過來的,而 windows從頭就是多線程的。node

2.多線程的優缺點

(1).   一個進程中全部的線程共享全局變臉和內存linux

(2).   程序邏輯和控制方便算法

(3).   線程方式消耗的總資源比進程要好數據庫

 

(1).   每一個線程和主程序共用地址空間,受限於2G地址空間編程

(2).  一個線程的崩潰可能影響到整個程序的穩定性;windows

(3).  到達必定的線程數程度後,即便再增長CPU也沒法提升性能,例如Windows Server 2003,大約是1500個左右的線程數就快到極限了(線程堆棧設定爲1M),若是設定線程堆棧爲2M,還達不到1500個線程總數; 線程可以提升的總性能有限,並且線程多了以後,線程自己的調度也是一個麻煩事兒,須要消耗較多的CPU 數組

3.多進程的優缺點

(1).   多個進程相互獨立,不影響程序的穩定性,子進程崩潰不要緊緩存

(2).   經過增長cpu能夠擴充性能安全

(3).   每一個子進程都有2GB地址空間和相關資源,整體可以到達的性能會比較大服務器

(4).   能夠儘可能減小線程加鎖/解鎖的影響,極大提升性能,就算是線程運行的模塊算法效率低也不要緊;

 

(1).  邏輯控制複雜,須要和主程序交互;

(2).  須要跨進程邊界,若是有大數據量傳送,就不太好,適合小數據量傳送、密集運算

(3).    多進程調度開銷比較大 

      最好是多進程和多線程結合,即根據實際的須要,每一個CPU開啓一個子進程,這個子進程開啓多線程能夠爲若干同類型的數據進行處理。固然你也能夠利用多線程+多CPU+輪詢方式來解決問題……方法和手段是多樣的,關鍵是本身看起來實現方便有可以知足要求,代價也合適

3.併發與程序類型

服務器併發:大量客戶端同時訪問,響應多個客戶端。

對於人數少的,數據交互量小的,單進程,單工做線程便可處理。

對於單核cpu,開多工做線程,在線程切換上是很影響效率的,可是對於不一樣的程序,單核多線程也可提核服務器的工做效率。

io密集型程序:工做線程處理的回調函數,處於io阻塞狀態,好比阻塞狀態的send recv,數據庫讀寫操作等,此時的io操作會阻塞線程,此時切換線程處理任務,能夠提高服務器效率。io密集型程序因爲大部分時間處於阻塞操作,cpu利用率很低,加多線程,可提高cpu利用率,利用多線程屏蔽掉線程阻塞時間。

cpu密集型程序:cpu利用率較高,程序任務並無太多io操作,多線程對其並無多大提高,相反,過多線程的切換,反而會浪費cpu時間,下降服務器效率

4.進程和線程上下文切換

進程切換分兩步

1.切換頁目錄以使用新的地址空間。

2.切換內核棧和硬件上下文。

對於linux來講,線程和進程的最大區別就在於地址空間。對於線程切換,第1步是不須要作的,第2是進程和線程切換都要作的。因此明顯是進程切換代價大。

線程上下文切換和進程上下問切換一個最主要的區別是線程的切換虛擬內存空間依然是相同的,可是進程切換是不一樣的。這兩種上下文切換的處理都是經過操做系統內核來完成的。內核的這種切換過程伴隨的最顯著的性能損耗是將寄存器中的內容切換出。另一個隱藏的損耗是上下文的切換會擾亂處理器的緩存機制。簡單的說,一旦去切換上下文,處理器中全部已經緩存的內存地址一瞬間都做廢了。還有一個顯著的區別是當你改變虛擬內存空間的時候,處理的頁表緩衝(processor’s Translation Lookaside Buffer (TLB))或者至關的神馬東西會被所有刷新,這將致使內存的訪問在一段時間內至關的低效。可是在線程的切換中,不會出現這個問題。

5.線程安全和可重入

線程安全:要確保函數線程安全,主要須要考慮的是線程之間的共享變量。屬於同一進程的不一樣線程會共享進程內存空間中的全局區和堆,而私有的線程空間則主要包括棧和寄存器。所以,對於同一進程的不一樣線程來講,每一個線程的局部變量都是私有的,而全局變量、局部靜態變量、分配於堆的變量都是共享的。在對這些共享變量進行訪問時,若是要保證線程安全,則必須經過加鎖的方式。

可重入:概念基本沒有比較正式的完整解釋,可是它比線程安全要求更嚴格。根據經驗,所謂「重入」,常見的狀況是,程序執行到某個函數foo()時,收到信號,因而暫停目前正在執行的函數,轉到信號處理函數,而這個信號處理函數的執行過程當中,又偏偏也會進入到剛剛執行的函數foo(),這樣便發生了所謂的重入。此時若是foo()可以正確的運行,並且處理完成後,以前暫停的foo()也可以正確運行,則說明它是可重入的

可重入與線程安全並不等同,通常說來,可重入的函數必定是線程安全的,但反過來不必定成立。

6. C10K問題的解決方案探討
要解決這一問題,從純網絡編程技術角度看,主要思路有兩個:

  • 一個是對於每一個鏈接處理分配一個獨立的進程/線程;
  • 另外一個思路是用同一進程/線程來同時處理若干鏈接。

1思路一:每一個進程/線程處理一個鏈接

這一思路最爲直接。可是因爲申請進程/線程會佔用至關可觀的系統資源,同時對於多進程/線程的管理會對系統形成壓力,所以這種方案不具有良好的可擴展性。
所以,這一思路在服務器資源尚未富裕到足夠程度的時候,是不可行的。即使資源足夠富裕,效率也不夠高。總之,此思路技術實現會使得資源佔用過多,可擴展性差。

2思路二:每一個進程/線程同時處理多個鏈接(IO多路複用)

IO多路複用從技術實現上又分不少種,咱們逐一來看看下述各類實現方式的優劣。
實現方式1:傳統思路最簡單的方法是循環挨個處理各個鏈接,每一個鏈接對應一個 socket,當全部 socket 都有數據的時候,這種方法是可行的。可是當應用讀取某個 socket 的文件數據不 ready 的時候,整個應用會阻塞在這裏等待該文件句柄,即便別的文件句柄 ready,也沒法往下處理。
實現小結:直接循環處理多個鏈接。
問題概括:任一文件句柄的不成功會阻塞住整個應用。
實現方式2:select要解決上面阻塞的問題,思路很簡單,若是我在讀取文件句柄以前,先查下它的狀態,ready 了就進行處理,不 ready 就不進行處理,這不就解決了這個問題了嘛?因而有了 select 方案。用一個 fd_set 結構體來告訴內核同時監控多個文件句柄,當其中有文件句柄的狀態發生指定變化(例如某句柄由不可用變爲可用)或超時,則調用返回。以後應用可使用 FD_ISSET 來逐個查看是哪一個文件句柄的狀態發生了變化。這樣作,小規模的鏈接問題不大,但當鏈接數不少(文件句柄個數不少)的時候,逐個檢查狀態就很慢了。所以,select 每每存在管理的句柄上限(FD_SETSIZE)。同時,在使用上,由於只有一個字段記錄關注和發生事件,每次調用以前要從新初始化 fd_set 結構體。

1

intselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

實現小結:有鏈接請求抵達了再檢查處理。
問題概括:句柄上限+重複初始化+逐個排查全部文件句柄狀態效率不高。
實現方式3:poll 主要解決 select 的前兩個問題:經過一個 pollfd 數組向內核傳遞須要關注的事件消除文件句柄上限,同時使用不一樣字段分別標註關注事件和發生事件,來避免重複初始化。
實現小結:設計新的數據結構提供使用效率。
問題概括:逐個排查全部文件句柄狀態效率不高。
實現方式4:epoll既然逐個排查全部文件句柄狀態效率不高,很天然的,若是調用返回的時候只給應用提供發生了狀態變化(極可能是數據 ready)的文件句柄,進行排查的效率不就高多了麼。epoll 採用了這種設計,適用於大規模的應用場景。實驗代表,當文件句柄數目超過 10 以後,epoll 性能將優於 select 和 poll;當文件句柄數目達到 10K 的時候,epoll 已經超過 select 和 poll 兩個數量級。
實現小結:只返回狀態變化的文件句柄。
問題概括:依賴特定平臺(Linux)。
由於Linux是互聯網企業中使用率最高的操做系統,Epoll就成爲C10K killer、高併發、高性能、異步非阻塞這些技術的代名詞了。FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了IOCP,Solaris推出了/dev/poll。這些操做系統提供的功能就是爲了解決C10K問題。epoll技術的編程模型就是異步非阻塞回調,也能夠叫作Reactor,事件驅動,事件輪循(EventLoop)。Nginx,libevent,node.js這些就是Epoll時代的產物。
實現方式5:因爲epoll, kqueue, IOCP每一個接口都有本身的特色,程序移植很是困難,因而須要對這些接口進行封裝,以讓它們易於使用和移植,其中libevent庫就是其中之一。跨平臺,封裝底層平臺的調用,提供統一的 API,但底層在不一樣平臺上自動選擇合適的調用。按照libevent的官方網站,libevent庫提供瞭如下功能:當一個文件描述符的特定事件(如可讀,可寫或出錯)發生了,或一個定時事件發生了,libevent就會自動執行用戶指定的回調函數,來處理事件。目前,libevent已支持如下接口/dev/poll, kqueue, event ports, select, poll 和 epoll。Libevent的內部事件機制徹底是基於所使用的接口的。所以libevent很是容易移植,也使它的擴展性很是容易。目前,libevent已在如下操做系統中編譯經過:Linux,BSD,Mac OS X,Solaris和Windows。使用libevent庫進行開發很是簡單,也很容易在各類unix平臺上移植。

相關文章
相關標籤/搜索