面經問題整理1

CDN

CDN:內容分配網絡,任何內容都可以通過CDN進行加速;
CDN概念:CDN是在現有internet上增加的新的一種網絡架構,好處是用戶可以在最近的節點上訪問到所需要的內容,加快了網站響應的速度。

那麼是如何做到的呢:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-snoCKiWF-1585662254322)(en-resource://database/826:1)]
上面是架構圖

下面來闡述過程:

  • 請求過程;當用戶想要訪問一個靜態文件如音樂,這個靜態文件的域名是cdn.qqmusic.com
  1. 首先,需要知道無CDN情況的的流程
    a.首先查詢瀏覽器的DNS緩存,看是否有映射的IP關係;沒有就查詢操作系統的DNS緩存,(操作系統的window在system32\drivers\etc\hosts下,linux在\etc\hosts下);沒有就訪問本地的域名服務器(接入互聯網的提供商);沒有就訪問根域名服務器,從根域名服務器得到頂級域名服務器的地址,然後訪問頂級域名服務器,從頂級域名服務器得到域名對應的Name Server的域名服務器地址,這個Name Server就存着這個域名對應的IP地址映射表,就可以返回IP地址了。

    如果有CDN,先訪問本地域名服務器(如果已經記錄了CNAME更快),會訪問到域名註冊的服務器,這時候DNS解析服務器就把這個域名的解析設置了CNAME,返回到local後發現有CNAME,就把請求轉發給CDN負載均衡服務器,服務器根據用戶的所在地分配最佳的CDN節點給用戶,用戶根據解析的IP去訪問CDN節點,CDN節點判斷緩存(二級緩存)中是否有數據,如果沒有就向實際IP地址提交訪問請求,然後CDN節點一方面返回用戶數據,另一方面在本地存儲。

CDN網絡一般分爲中心節點和邊緣節點;
中心節點的功能就是負載均衡、監控邊緣節點健康狀況、根據邊緣節點狀況重定向請求,與主站同步更新。
邊緣節點負責保存着緩存的內容,有二級緩存,當邊緣節點未命中內容時,就會向中心層請求。

CDN如果單節點負載過大,那麼中心節點會進行調整,

IO機制

阻塞式IO和非阻塞式IO (見unix內容)

IO多路複用

  • select o(n)
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

fd_set可以理解爲一個集合,這個集合中存放的是文件描述符(file descriptor)
fd0 fd1
readset 1 0 …

關鍵用於判斷IO是否已經完成即數據可讀寫的標誌就在 readset writeset裏。調用select時會把fd_set集合都放到內核態中。

select相對於同步阻塞模型的好處是,他可以簡單實現多個socket的IO請求同時處理,用戶可以註冊多個socket,然後不斷調用select來讀取被激活的socket。而同步阻塞模型就只能通過多線程(不能用多進程)完成,多線程同步上比較難實現。

問題
1.fd_set集合如果很大那麼複製到內核態開銷很大,而且頻繁調用的話
2.每次在內核態中都要遍歷fd_set,如果fd_set很大,遍歷的過程開銷大
3. 爲了減少數據拷貝帶來的性能損壞,內核對被監控的fd_set集合大小做了限制1024

  • poll o(n)
    和select最大的區別是取消了fd_set大小的限制。
    基於鏈表來存儲的.

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

typedef struct pollfd {
        int fd;                         // 需要被檢測或選擇的文件描述符
        short events;                   // 對文件描述符fd上感興趣的事件
        short revents;                  // 文件描述符fd上當前實際發生的事件} pollfd_t;

使用了pollfd的結構,使得文件描述符集合能夠大於1024

  • epoll o(1)
    特點:回調 哈希表 只拷貝一次fd,之後每次epoll_wait不拷貝。
    採用事件通知的方式,當fd就緒,系統註冊的回調函數就會被調用,將就緒的fd放到readylist裏面。

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epoll_create 創建一個epoll句柄,size表示要監聽的描述符數量
epoll_ctl 註冊要監聽的事件類型
epoll_wait 函數等待事件就緒

獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了

水平觸發LT: 當epoll_wait檢測到fd就緒並通知應用程序時,應用程序可以不馬上處理該事件,等下一次epoll_wait的時候再次通知該事件

邊緣觸發ET:當epoll_wait檢測到某描述符事件就緒並通知應用程序時,應用程序必須立即處理該事件。如果不處理,下次調用epoll_wait時,不會再次通知此事件。
邊緣觸發優點體現在:當系統中有大量不需要讀寫的就緒文件描述符的時候,每次調用epoll_wait就都會返回,這樣效率會很低;但是必須要注意一次讀取就要把所有數據都讀出來。

對select缺點的解決

  1. 多次複製fd_set問題。epoll只會在調用epoll_ctl函數時,即註冊新的事件到epoll對象裏面的時候纔會複製fd,就不會每次輪詢都要複製。
    2.遍歷fd_set問題。 epoll在調用epoll_ctl的時候給每個fd設置了回調函數 當設備就緒,喚醒等待隊列上的等待者時,就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒鏈表。
    epoll_wait的工作實際上就是在這個就緒鏈表中查看有沒有就緒的fd(利用schedule_timeout()實現睡一會,判斷一會的效果。

  2. 相對於select來說,epoll沒有描述符個數限制,使用一個文件描述符管理多個描述符,將用戶關心的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的copy只需一次。

如何追蹤運行期間進程的堆棧 gdb的原理

依賴於系統函數ptrace,就是能觀察寄存器和內存的內容。
斷點的功能是通過內核信號實現的,以x86爲例,內核向某個地址打入斷點,實際上就是往該地址寫入斷點指令INT 3,即0xCC。到目標程序運行到該斷點處時,產生SIGTRAP信號,該信號被GDB捕獲,GDB查找斷點列表來確定是否命中斷點。繼續執行的時候則會把保存的指令重新放回並執行.

gdb維護一個斷點鏈表,如果gdb捕獲到斷點的信號,那麼就說明有中斷。
b + 行號 next step continue watch print

gdb是怎麼工作的,和內核什麼地方有關係?
ptrace的系統調用來實現的,使得父進程能夠觀察子進程的執行,檢測和改變其核心映像和寄存器。

下面我們來看ptrace函數中request參數的一些主要選項:PTRACE_TRACEME: 表示本進程將被其父進程跟蹤,交付給這個進程的所有信號,即使信號是忽略處理的(除SIGKILL之外),都將使其停止,父進程將通過wait()獲知這一情況。這是什麼意思呢?我們可以結合到gdb上來看。如果在gdb中run一個程序,首先gdb會fork一個子進程,然後該子進程調用ptrace系統調用,參數就是PTRACE_TRACEME,然後調用一個exec執行程序。基本過程是這樣,細節上可能會有出入。需要注意的是,這個選項PTRACE_TRACEME是由子進程調用的而不是父進程!

trace系統調從名字上看是用於進程跟蹤的,它提供了父進程可以觀察和控制其子進程執行的能力,並允許父進程檢查和替換子進程的內核鏡像(包括寄存器)的值。其基本原理是:
在被調試程序和gdb之間建立跟蹤關係,當使用了ptrace跟蹤後,所有發送給被跟蹤的子進程的信號(除了SIGKILL),都會被轉發給父進程,而子進程則會被阻塞,這時子進程的狀態就會被系統標註爲TASK_TRACED。而父進程收到信號後,就可以對停止下來的子進程進行檢查和修改,然後讓子進程繼續運行。

單步跟蹤是在每一步執行完一條指令之後就觸發一個SIGTRAP信號,讓GDB運行

fork 父進程得到的是子進程pid 子進程得到0,。
複製一個虛擬地址空間出來,要修改的時候採用寫時複製技術。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章