服務器面試題

前些時間去了騰訊面試, 可惜現場沒回答好。
是一些基礎問題,同時也比較深入的問題。 在此列出來, 歡迎大家討論交流。


提問(不按時間順序):

1, 使用Linux epoll模型,水平觸發模式(Level-Triggered);當socket可寫時,會不停的觸發socket可寫的事件,如何處理?

2, 從socket讀數據時,socket緩存裏的數據,可能超過用戶緩存的長度,如何處理? 例如,socket緩存有8kB的數據,而你的緩存只有2kB空間。

3, 向socket發送數據時, 可能只發送了用戶緩存裏的一半,如何處理?例如,需要向socket發送8kB數據,返回值只有2kB發送成功。


4, C++的虛函數是怎麼實現的?

5, C++的虛函數有什麼作用?

6, 非阻塞connect()如何實現?

7,sizeof()問題

class A
{
  char c;
  int   val;
  short sh;
}


class B
{
    char c;
    int   val;
    short sh;
    void func1(void);
    virtual func2(void);
}

sizeof(A), sizeof(B) 分別是多少?

8, 實現字符串比較函數  strcmp(char *src, char * sub)

9, 實現內存拷貝函數  strcpy(void*dst, char * src, size_t len)

10,條件變量的如何使用? 你使用的線程函數是什麼?

11, deamon進程如何實現?

12, HTTP和CGI是什麼?

13, TCP的三次握手, TIME_WAIT和CLOSE_WAIT狀態是什麼?


因爲第7題之後的屬於客觀題,不打算在此寫答案。 朋友們如有好的答案也歡迎跟貼。

本人在此寫出自己對前6個問題的回答:

1, 使用linux epoll模型,水平觸發模式(Level-Triggered);當socket可寫時,會不停的觸發socket可寫的事件,如何處理?

第一種最普通的方式:  
    當需要向socket寫數據時,將該socket加入到epoll模型(epoll_ctl);等待可寫事件。
    接收到socket可寫事件後,調用write()或send()發送數據。。。 
    當數據全部寫完後, 將socket描述符移出epoll模型。
   
    這種方式的缺點是:  即使發送很少的數據,也要將socket加入、移出epoll模型。有一定的操作代價。

第二種方式,(是本人的改進方案, 叫做directly-write)

    向socket寫數據時,不將socket加入到epoll模型;而是直接調用send()發送;
    只有當或send()返回錯誤碼EAGAIN(系統緩存滿),纔將socket加入到epoll模型,等待可寫事件後,再發送數據。
    全部數據發送完畢,再移出epoll模型。

     這種方案的優點:   當用戶數據比較少時,不需要epool的事件處理。 
     在高壓力的情況下,性能怎麼樣呢?   
      對一次性直接寫成功、失敗的次數進行統計。如果成功次數遠大於失敗的次數, 說明性能良好。(如果失敗次數遠大於成功的次數,則關閉這種直接寫的操作,改用第一種方案。同時在日誌裏記錄警告)
     在我自己的應用系統中,實驗結果數據證明該方案的性能良好。
     
    事實上,網絡數據可分爲兩種到達/發送情況:
     一是分散的數據包, 例如每間隔40ms左右,發送/接收3-5個 MTU(或更小,這樣就沒超過默認的8K系統緩存)。
     二是連續的數據包, 例如每間隔1s左右,連續發送/接收 20個 MTU(或更多)。

回來查了資料,發現以下兩種方式:

    第三種方式:  使用Edge-Triggered(邊沿觸發),這樣socket有可寫事件,只會觸發一次。 
             可以在應用層做好標記。以避免頻繁的調用 epoll_ctl( EPOLL_CTL_ADD, EPOLL_CTL_MOD)。  這種方式是epoll 的 man 手冊裏推薦的方式, 性能最高。但如果處理不當容易出錯,事件驅動停止。

第四種方式:  在epoll_ctl()使用EPOLLONESHOT標誌,當事件觸發以後,socket會被禁止再次觸發。
             需要再次調用epoll_ctl(EPOLL_CTL_MOD),纔會接收下一次事件。   這種方式可以禁止socket可寫事件,應該也會同時禁止可讀事件。會帶來不便,同時並沒有性能優勢,因爲epoll_ctl()有一定的操作代價。


2, 從socket讀數據時,socket緩存裏的數據,可能超過用戶緩存的長度,如果處理?
       可以調用realloc(),擴大原有的緩存塊尺寸。 
       但是臨時申請內存的有一定性能損失。

      這種情況要看接收緩存的方式。
第一種方式:  使用100k的大接收緩存爲例。 
               如果要等待數據,並進行解析。可能發生緩存不夠的情況。此時只能擴充緩存,或先處理100k的數據,再接收新的數據。
第二種方式: 使用緩存隊列,分成8K大小的隊列。
               不存在接收緩存不夠的情況。 除非用戶解析已出錯,使用數據接收、使用脫勾。 這種方式的代價是,可能需要將緩存隊列再次拷貝、拼接成一塊大的緩存,再進行解析。 而在本人的系統中,只需要將socket接收的數據再次原樣分發給客戶, 所以這種方案是最佳方案。

3, 向socket發送數據時, 可能只發送了用戶緩存裏的一半,然後失敗,如何處理?

      記錄緩存的偏移量。 下一次socket寫事件時, 再從偏移的位置接着發送。
      
       那個面試官居然對這個問題問了我兩次, 看來我解釋的不夠清晰。。。。。。 鬱悶。

4, C++的虛函數是怎麼實現的?
       使用虛函數表。
       回來查下資料:  C++對象使用虛表, 如果是基類的實例,對應位置存放的是基類的函數指針;如果是繼承類,對應位置存放的是繼承類的函數指針(如果在繼承類有實現)。所以,當使用基類指針調用對象方法時,也會根據具體的實例,調用到繼承類的方法。

5, C++的虛函數有什麼作用?

        虛函數作用是實現多態, 很多人都能理解這一點。但卻不會回答下面這一點。

       更重要的,虛函數其實是實現封裝,使得使用者不需要關心實現的細節。在很多設計模式中都是這樣用法,例如Factory、Bridge、Strategy模式。 前兩天在書上剛好看到這個問題,但在面試的時候卻沒想起來。
        個人覺得這個問題可以很好的區分C++的理解水平。

6, 非阻塞connect()如何實現?
       將socket設置成non-blocking,操作方法同非阻塞read()、write();
       面試官是在聽到我介紹之後,才問我這個問題。可惜還是問我兩遍。 




這次面試, 總的來說準備不夠充足, 所以這次機會沒有青睞我!
也有其它一些問題:
1, 對於一般的面試提問, 總是想很簡要的回答完。因爲對方可能本來就很清楚,所以自己就想一兩句話說完。 但是有時候這樣行不通。需要適當的回答清晰、完整一些。
2, 對TCP/UDP的問題本來是很熟悉的,但因爲長時間沒複習,忘的差不多了。
3, 以前已經對RTSP進行了仔細的學習。 HTTP、SIP屬於同一類協議。而我卻回答不了HTTP的問題。努力學習啊................
4, 有些問題要問我兩遍,說明我的表達確實不夠清晰。有的問題可能面試官自己並不清晰,所以除了表達清晰之外,完全有必要適當的回答稍完整些。否則很難讓人滿意。
5, 精神狀態不太好,思維有些慢了。 因爲總是睡的晚。

接下來打算繼續研究 lighttpd源碼, 這樣對我自己的水平提高會有很大幫助。

機會總是青睞有準備的人! 期待下次。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章