<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          2020校招面試

          共 6612字,需瀏覽 14分鐘

           ·

          2020-09-10 20:02

          個人情況

          • 崗位:后臺 golang 開發(fā)

          • 公司:字節(jié) shopee 拼多多百度阿里快手都有

          • 途徑:提前批內(nèi)推

          • 學(xué)歷:某 985 大三

          • 實習(xí)情況:一段小廠實習(xí)

          • 項目和準(zhǔn)備:實習(xí)時候做的是一個分布式存儲系統(tǒng)以及一個 kv 數(shù)據(jù)庫,這也是面試的重點

          計算機(jī)網(wǎng)絡(luò)

          1,tcp 中 timewait 狀態(tài)的作用,為什么要等待兩個 msl

          2,tcp 中三次揮手開啟連接,四次握手關(guān)閉連接的流程

          3,聊聊 tcp 的滑動窗口

          4,ssl 建立過程

          5,輸入一個 url 的過程

          6,大文件傳輸

          1,答:為了確認(rèn)被動關(guān)閉端接受到最后一個ack,避免主動關(guān)閉端重新在相同端口啟動連接后發(fā)送syn后被動關(guān)閉端認(rèn)為上一個連接沒有完全關(guān)閉,進(jìn)而返回rst終止連接

          2,答:就是那個著名的連接建立圖

          3,答:滑動窗口由接受窗口和擁塞窗口中的最小值,然后就是reno的慢啟動,擁塞控制,快速重傳三個步驟。然后我還談了談cubic算法。

          4,答:很詳細(xì)的描述。從客戶端發(fā)送clienthello包括ssl版本,對稱算法,第一個不重數(shù),mac算法,公鑰算法。重點是一共生成了三個不重復(fù)數(shù),從主密鑰解出了四個密鑰,兩個用于會話加密,兩個用于mac加密。為什么是兩個呢,因為一個用于客戶端到服務(wù)器的會話加密,另一個用于服務(wù)器到客戶端的會話加密。這里要提醒證書機(jī)制并不是完全安全的,因此有EXPECT_CT這個瀏覽器的頭,防止證書頒發(fā)機(jī)構(gòu)被劫持。

          5,答:從dns從瀏覽器,操作系統(tǒng)host文件解析。到http的hsts連接建立過程(302,307等狀態(tài)碼),到瀏覽器緩存etag,以及dom樹和css樹解析,繪圖和渲染,js事件循環(huán)https://juejin.im/post/6844903922084085773。

          6,答:文件分塊。服務(wù)端返回206表示部分?jǐn)?shù)據(jù),416表示范圍出錯。添加一個Range header表示發(fā)送的數(shù)據(jù)的范圍。

          linux io

          首先需要對 linux 五種 io 模型和 epoll 有一定的了解,這里推薦一篇文章https://juejin.im/post/5c725dbe51882575e37ef9ed。

          在傳輸文件的時候,有 sendfile 語意,用于高效的傳輸文件。

          減少了內(nèi)核上下文切換以及 cpu 復(fù)制的損耗。

          這里引用一篇文章https://juejin.im/post/6844903949359644680#heading-17。

          在 go 語言里頭,當(dāng)我們使用 io.copy 的時候,會判斷目標(biāo)是否實現(xiàn) readerFrom 接口。

          如果實現(xiàn)了就會調(diào)用 readerFrom,那么 readerFrom 和普通磁盤 io 的區(qū)別在哪呢?

          // copyBuffer is the actual implementation of Copy and CopyBuffer.
          // if buf is nil, one is allocated.
          func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
          // If the reader has a WriteTo method, use it to do the copy.
          // Avoids an allocation and a copy.
          if wt, ok := src.(WriterTo); ok {
          return wt.WriteTo(dst)
          }
          // Similarly, if the writer has a ReadFrom method, use it to do the copy.
          if rt, ok := dst.(ReaderFrom); ok {
          return rt.ReadFrom(src)
          }

          在 go 語言里頭,tcpconn 實現(xiàn)了這個接口。我們會發(fā)現(xiàn)它會用 splice 和 sendfile 兩個系統(tǒng)調(diào)用去獲取數(shù)據(jù)。如果 sendfile 系統(tǒng)也不支持,那么就會做一個優(yōu)雅降級的處理,轉(zhuǎn)換為普通的 io.Copy(隱藏 readerFrom 接口)。

          func (c *TCPConn) readFrom(r io.Reader) (int64, error) {
          if n, err, handled := splice(c.fd, r); handled {
          return n, err
          }
          if n, err, handled := sendFile(c.fd, r); handled {
          return n, err
          }
          return genericReadFrom(c, r)
          }
          普通的磁盤 io 傳輸。

          一,發(fā)起 read 系統(tǒng)調(diào)用,從用戶態(tài)切換到內(nèi)核態(tài)。

          二,cpu 通過控制 dma,將數(shù)據(jù)從硬件緩沖區(qū) copy 到內(nèi)核緩沖區(qū)。再將內(nèi)核緩沖區(qū)的數(shù)據(jù) copy 到用戶緩沖區(qū)。

          三,系統(tǒng)調(diào)用結(jié)束,從內(nèi)核態(tài)切換為用戶態(tài)。

          四,發(fā)起 write 系統(tǒng)調(diào)用,同上。

          整個操作兩次 cpu copy,兩次 dma copy,四次上下文切換,兩次系統(tǒng)調(diào)用。



          sendfile

          一,sendfile 系統(tǒng)調(diào)用,從用戶態(tài)切換到內(nèi)核態(tài)

          二,數(shù)據(jù)從硬件緩沖區(qū)通過 dma copy 復(fù)制到內(nèi)核緩沖區(qū);網(wǎng)卡的硬件緩沖區(qū)直接從磁盤對應(yīng)數(shù)據(jù)的內(nèi)核緩沖區(qū)讀取數(shù)據(jù)

          三,sendfile 調(diào)用結(jié)束,從內(nèi)核態(tài)切換為用戶態(tài)

          整個過程兩次 dma copy,兩次上下文切換,0 次 cpu copy,一個系統(tǒng)調(diào)用。



          socket

          而關(guān)于 tcp 這塊,socket 編程也需要了解,下面我們就來看看 go 中 socket 編程的流程。

          net.dialTcp 是如何包裝 linux 系統(tǒng)調(diào)用的 socket 的。

          客戶端

          • 使用 socket 建立連接

          linux 操作系統(tǒng)把各種 tcp,udp 連接抽象化成 socket,而 go 通過調(diào)用 linux 系統(tǒng)調(diào)用來建立連接。linux 中一切節(jié)文件,SYS_SOCKET 返回的一個數(shù)字就代表著文件的進(jìn)程打開文件描述符的句柄。type 表示 socket 類型,我們建立一個 tcp 連接,就使用的是 SOCK_STREAM,表示一個流式連接;proto 表示協(xié)議,IPPROTO_TCP 表示連接傳輸協(xié)議。domain 表示協(xié)議域,AF_INET、AF_INET6 表示 ip4,ip6 的協(xié)議。

          func socket(domain int, typ int, proto int) (fd int, err error) {
          r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
          fd = int(r0)
          if e1 != 0 {
          err = errnoErr(e1)
          }
          return
          }
          • 將 socket 注冊為 nonblock 和 closeonexec 形式。當(dāng)我們向 socket 讀取數(shù)據(jù)的時候,是從設(shè)備緩沖區(qū)到內(nèi)核緩沖區(qū)再到用戶緩沖區(qū),而 nonblock 會當(dāng)設(shè)備緩沖區(qū)中數(shù)據(jù)沒有準(zhǔn)備好時返回一個 EAGAIN 錯誤。closeonexec 可以參考這篇文章https://blog.csdn.net/ljxfblog/article/details/41680115

          syscall.CloseOnExec(s)
          syscall.SetNonblock(s, true)
          • 在 epoll 注冊 socket。當(dāng)向 socket 寫入數(shù)據(jù)的時候,首先使用系統(tǒng)調(diào)用 write,如果返回 eagain 錯誤,則休眠當(dāng)前 goroutine,等待 epoll 喚醒。

          • 注冊為 nodelay(禁止 nigle 算法)

          • dodialtcp 用于建立一條 tcp 連接,包括本端地址和外目標(biāo)地址。internetsocket 就是完成我們之前所說的事。之后的錯誤處理主要是因為,當(dāng)建立連接時如果沒有源端口,那么就會隨機(jī)選擇一個端口,由于 tcp 能夠同時建立連接;那么很可能出現(xiàn)一條連接,目的端口和接受端口一致而且目的地址和接受地址一樣的情況。我們這里就是為了避免出現(xiàn)這種情況


          func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
          fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control)

          // TCP has a rarely used mechanism called a 'simultaneous connection' in
          // which Dial("tcp", addr1, addr2) run on the machine at addr1 can
          // connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
          // at addr2, without either machine executing Listen. If laddr == nil,
          // it means we want the kernel to pick an appropriate originating local
          // address. Some Linux kernels cycle blindly through a fixed range of
          // local ports, regardless of destination port. If a kernel happens to
          // pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"),
          // then the Dial will succeed, having simultaneously connected to itself.
          // This can only happen when we are letting the kernel pick a port (laddr == nil)
          // and when there is no listener for the destination address.
          // It's hard to argue this is anything other than a kernel bug. If we
          // see this happen, rather than expose the buggy effect to users, we
          // close the fd and try again. If it happens twice more, we relent and
          // use the result. See also:
          // https://golang.org/issue/2690
          // https://stackoverflow.com/questions/4949858/
          //
          // The opposite can also happen: if we ask the kernel to pick an appropriate
          // originating local address, sometimes it picks one that is already in use.
          // So if the error is EADDRNOTAVAIL, we have to try again too, just for
          // a different reason.
          //
          // The kernel socket code is no doubt enjoying watching us squirm.
          for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
          if err == nil {
          fd.Close()
          }
          fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control)
          }

          if err != nil {
          return nil, err
          }
          return newTCPConn(fd), nil
          }

          服務(wù)器

          • 使用 socket 建立一條連接,使用 listen 將 socket 注冊為 listen 狀態(tài),bind 系統(tǒng)調(diào)用綁定端口

          • 通過 accept 從 socket 接受連接。accept 首先通過系統(tǒng)調(diào)用 accept 等待連接,如果返回 eagain 錯誤則 epoll 等待。如果是 connectaborted 錯誤,則重試。connectionaborted 表示 tcp“三次握手” 后,又發(fā)送了一個 rst 中斷連接。

          • 對于通過 accept 接受到的連接,也僅僅是一個 int 類型的 fd,我們要通過 epoll 包裝連接。

          • 然后設(shè)置 keepalive 為默認(rèn)的 15s 間隔。keepalive 設(shè)置原則可以參考這里https://github.com/golang/go/issues/23459keepcnt 的不同,因此我們將 keepalive 設(shè)置為 15s,在 keepcnt 最大為 9 的 linux 系統(tǒng)上,超時事件為 150s。主要為了適應(yīng)不同操作系統(tǒng)上 < 3min。

          func (ln *TCPListener) accept() (*TCPConn, error) {
          fd, err := ln.fd.accept()
          if err != nil {
          return nil, err
          }
          tc := newTCPConn(fd)
          if ln.lc.KeepAlive >= 0 {
          setKeepAlive(fd, true)
          ka := ln.lc.KeepAlive
          if ln.lc.KeepAlive == 0 {
          ka = defaultTCPKeepAlive
          }
          setKeepAlivePeriod(fd, ka)
          }
          return tc, nil
          }


          學(xué)習(xí)經(jīng)驗

          書籍推薦《計算機(jī)網(wǎng)絡(luò)-自頂向下》。

          一些業(yè)界比較新的算法也要會:cubic,bbr,quic。quic 推薦這篇文章https://zhuanlan.zhihu.com/p/32553477bbr 推薦這篇文章 https://www.jianshu.com/p/08eab499415a。,

          tcp 的一些 option 字段要了解:sack,timestamp。

          關(guān)于學(xué)習(xí) http 中繁瑣的 header,這里推薦使用 chrome 瀏覽器觀察 GitHub 的 http 連接建立的過程,觀察使用了哪些 header,如何使用 cookie 的(ps:可以學(xué)到很多的瀏覽器安全比如 xss 等知識)

          而很多知識比如說 socket 的連接建立其實光看博客很抽象化,最好去看看 go 語言內(nèi)部的源碼。而往往在看 go 源碼的時候又會順藤摸瓜學(xué)習(xí)到很多知識。

          面試經(jīng)驗

          一,一些重要的數(shù)據(jù)結(jié)構(gòu)題還是要背一背的,不然面試官說讓你寫一個堆排序,事實上是讓你寫一個 for 循環(huán)的,考慮到 int 類型溢出的排序,這里沒有提前的準(zhǔn)備,很難當(dāng)場寫好。

          二,有些問題可能你覺得自己專門準(zhǔn)備過,準(zhǔn)備好滔滔不絕顯示能力。但很多時候面試官不喜歡你說的太多,因為說的太多很像背面經(jīng)。。。因此首先要簡略的回答出重點,再看看面試官的反應(yīng)如何,考慮是否需要詳細(xì)的說。

          三,針對項目,要準(zhǔn)備的非常深,詳細(xì)到重要參數(shù)的大小,為什么這樣設(shè)置?當(dāng)然,實際上很多參數(shù)其實也沒有個明確標(biāo)準(zhǔn)。。。比如我做存儲項目的時候,要在一個目錄下存儲很多大文件,然后文件分片成 256kb。。。面試官就問為什么分片成 256kb,為什么不能分片成 4kb 或者 16kb??當(dāng)時就懵了,后面網(wǎng)上查了查,發(fā)現(xiàn)創(chuàng)作者也是照著其他開源軟件的標(biāo)準(zhǔn)設(shè)置的。。。

          四,總有一些問題你從來就沒見過的。遇見不要慌,說回自己熟悉的領(lǐng)域。比如說面試官曾經(jīng)問我 c++ 里頭怎么做 io 隔離?當(dāng)時想了半天也沒弄明白到底什么是 io 隔離,所以我感覺 go 中沒有這個問題。于是我就只好說抱歉我不懂 c++,但我可以談?wù)?go 語言是如何封裝 linux 系統(tǒng)文件系統(tǒng)調(diào)用,我們項目是如何處理文件的讀寫。。。然后在我們這種情況下,應(yīng)該是不存在 io 隔離的問題等等。最后也順利過了面試。

          最后,希望每個 gopher 都能在求職季收獲自己滿意的 offer。


          目前Gopher China大會已開啟。關(guān)于大會商務(wù)合作,請聯(lián)系聯(lián)系司徒小姐姐:18516100522 ??


          關(guān)于大會報名,請點擊“閱讀原文”進(jìn)入活動頁面。但由于目前信息不夠齊備, 也請保持對我們的關(guān)注,后續(xù)會揭開更多驚喜。


          最后,希望每位開發(fā)者永遠(yuǎn)保持不斷提升自己的動力,因為無論外界如何,技術(shù)實力就是我們的底氣。

          瀏覽 130
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产青青草自拍视频 | A片免费观看网站 | 久久久久久亚洲AV无码蜜芽老妇 | 欧美一区二区在线视频 | 一本色道久久爱牛牛 |