510 likes | 669 Views
Socket I/O. Introduction. 討論在網路連接上進行資料讀寫的系統呼叫 分成三部分來討論 1. 傳送資料 (write, writev, sendto, sendmsg) 的系統呼叫 2. 接收資料 (read, readv, recvfrom, recvmsg) 的系統呼叫 3. select 系統呼叫,用來監看 descriptor. Code Introduction. Socket Buffer. sockbuf data structure. Socket Buffer.
E N D
Introduction • 討論在網路連接上進行資料讀寫的系統呼叫 • 分成三部分來討論 • 1. 傳送資料(write, writev, sendto, sendmsg)的系統呼叫 • 2. 接收資料(read, readv, recvfrom, recvmsg)的系統呼叫 • 3. select系統呼叫,用來監看descriptor
Socket Buffer • sockbuf data structure
Socket Buffer • 每一個緩衝區中,除了指向儲存資料的mbuf chain外,還含有控制資訊 • sb_mb: 指向mbuf chain中的第一個mbuf • sb_cc: 紀錄包含在mbuf中的資料總位元數 • sb_hiwat/ sb_lowat : 拿來調節socket的流量控制演算法 • sb_mbcnt: 在這一個mbuf中,總共配置了多少記憶體空間給mbuf • sb_mbmax: 在每一個socket緩衝區裡面,要配置給mbuf之記憶體空間的上限值。系統核心的絕對極限值(sb_max: 262144 bytes)
Socket Buffer • sb_sel: 用來實作select系統呼叫的selinfo資料結構 • sb_timeo: 限制一個行程在進行讀取、或是寫入之呼叫,所能被停頓的時間(ticks)。預設值是0(無限期的等待)
Socket Buffer • sb_flags:
write, writev, sendto, and sendmsg System Call • 寫入系統呼叫 • write, writev, sendto, and sendmsg • 負責在一個網路連接上傳送資料 • 會直接或間接的呼叫sosend函式 • sosend會從行程中複製資料到系統核心中,並將資料遞交給相關的通訊協定
writev, sendmsg可以接受來自多重緩衝區的資料 • 從多重緩衝區進行寫入稱為收集(gathering) • 系統核心會從iovec資料結構陣列所指定的各種緩衝區當中依序去接收資料
Iov_base: 指向一個長度為iov_len個位元的緩衝區開頭 • 如果沒有這一種型態的介面 • 行程得先將各緩衝區的資料複製到一個較大的緩衝區 • 得呼叫多次的寫入系統呼叫 • 以上兩種都較沒有效率
sendmsg支援控制資訊 • 控制資訊、以及一些要傳遞給sendmsg的參數,都是在一個msghdr資料結構中被指定的
sendmsg System Call • sendmsg, sendit會負責準備sosend函式呼叫所需要的資料結構,sosend會把這一些訊息交給適當的通訊協定 • SOCK_DGRAM: 一份訊息就是一個資料包 • SOCK_STREAM: 一份訊息就是一連串的位元組 • copy iov array • sendit and cleanup
sendit Function Call • 被sendto, sendmsg這二個system call呼叫 • sendit會去初始化一個uio資料結構 • 將control, address 資訊從行程中複製到系統核心 • Initial auio • 會去呼叫getsock函式,取得descriptors 相關的file資料結構 • 初始化uio資料結構,把行程所指定的輸出緩衝區收集到系統核心當中的mbufs • 把各個緩衝區的長度大小加總,並且存在uio_resid • Copy address and control information from the process • Send data and cleanup
sosend Function Call • 5個寫入系統呼叫,最後會呼叫sosend • sosend要將資料、控制資訊,根據通訊協定以及socket指定的緩衝區限制值,遞交給與這一個socket相關的通訊協定的pr_usrreq函式 • sosend不會將資料放到緩衝區,通訊協定要自行儲存、移除這一些資料 • sosend會根據相關的通訊協定是否有實作可信賴或是不可信賴的資料傳輸語意,來定義緩衝區的sb_hiwat以及sb_lowat
sosend Function Call • 可靠通訊協定緩衝區處理 • 緩衝區中儲存了還沒有被送出去的資料、以及已經傳送出去但是還沒被ACK的資料 • sb_cc: 緩衝區中所儲存的資料位元數目0 <= sb_cc <= sb_hiwat • 如果有任何資料要透過pr_usrreq傳遞給通訊協定,sosend會確保緩衝區中有足夠的空間,通訊協定層會把資料添加到傳送緩衝區
sosend會根據以下兩種方式當中一種,把資料傳輸給通訊協定sosend會根據以下兩種方式當中一種,把資料傳輸給通訊協定 • PR_ATOMIC被設定: sosend必須在行程和通訊協定之間保留訊息的邊界。sosend會去等待足夠的空間來儲存整個訊息,當有足夠空間可使用,一條包含整個訊息的mbuf chain就會建造出來,並透過pr_usereq在單一次呼叫中遞交給通訊協定,RDP便是這一類的通訊協定。 • PR_ATOMIC沒被設定: sosend就會把訊息一次遞交一個mbuf給通訊協定,也有可能遞交部分的mbuf,避免超出高水位標,TCP這一類的SOCK_STREAM通訊協定所使用。
sosend Function Call • 不可靠通訊協定緩衝區處理 • 沒有任何資料會儲存在傳送緩衝區,也不會預期去收任何的ACK • 每一份訊息,都是直接被遞交給通訊協定,放到適合的網路裝置佇列等候傳送 • sb_cc永遠為0,sb_hiwat則是指出每一次最大寫入的長度大小也間接指出資料包的最大長度 • UDP來說,sb_hiwat的預設值是9216,除非用SO_SNDBUF這一個socket選項去修改
sosend Function Call • 流程 • 初始化 • 錯誤與資源的檢查 • 資料傳輸 • 通訊協定分發
sosend Function Call uiomove Function • int uiomove(cassr_t cp, int n, struct uio *uio) • uiomove會在cp所參考到的單一一個緩衝區,與uio裡頭iovec陣列所指定的多重緩衝區之間,搬移n個位元
sosend Function Call • 發出pr_usrreq (PRU_SEND) • tcp_usrreq() • 將要送的資料添加到so_snd緩衝區中 • 呼叫tcp_output
read, readv, recvfrom, and recvmsg System Call • 讀取系統呼叫 • read, readv, recvfrom, and recvmsg • 負責在一個網路連接上接收資料 • 會直接或間接的呼叫soreceive函式
read, readv可以和任何型態的descriptor搭配使用,其他的便只能搭配socket descriptor使用 • 像寫入系統呼叫一樣,多重的緩衝區是由一個iovec資料結構的矩陣來指定的
和寫入系統呼叫一樣,讀取系統呼叫都會去使用一個共通的函式呼叫(soreceive)來進行所有的工作和寫入系統呼叫一樣,讀取系統呼叫都會去使用一個共通的函式呼叫(soreceive)來進行所有的工作
recvmsg System Call • recvmsg是最一般化的讀取系統呼叫 • copy iov array • 會將msghdr資料結構複製到系統核心,如果自動配置的aiov太小的話將配置一個較大的iovec矩陣,並且從行程裡複製矩陣的紀錄項到iov所指向的系統核心的矩陣當中 • recvit and cleanup • recvit接收到資料之後,msghdr資料結構會連同更新的緩衝區長度以及旗標被複製回行程中
recvit Function Call • 被recv, recvfrom, recvmsg所呼叫 • 依據recvxxx系統呼叫所準備的msghdr資料結構,去準備uio給sorecvive去進行處理
recvit Function Call • 執行過程 • 利用getsock函式呼叫回傳descriptor s的File資料結構 • 初始化一個uio來描述一個從系統核心到行程的讀取傳送 • 呼叫soreceive • 實作了從socket緩衝區接收資料 • 傳送的位元組總數會被儲存在*retsize中,並回傳給行程 • 複製位址以及資訊到行程裡 • 如果行程為位址或是控制資訊提供了緩衝區,那麼這一些緩衝區就會被填寫,長度也會依據soreceive的傳回值加以調整 • 清除
soreceive Function Call • 從socket的接收緩衝區傳送資料到行程所指定的緩衝區當中 • 遞交給系統核心的flag值 • MSG_DONTWAIT: 再這一次系統呼叫中,不要等待資源 • MSG_OOB: 接收頻外資料,而非一般資料 • MSG_PEEK: 接收資料的副本,並且不去消耗資料本身 • MSG_WAITALL: 在返回之前,先等待資料填入緩衝區
soreceive Function Call • 系統核心傳回的flag值 • MSG_CTRUNC: 所收到的控制訊息長度,筆所提供的緩衝區長度還大 • MSG_EOR: 所接收到的資料標記了一筆邏輯紀錄的尾端 • MSG_OOB: 在緩衝區當中,包含了頻外資料 • MSG_TRUNC: 所收到的訊息長度,比提供的緩衝區長度還要大
soreceive Function Call • 頻外資料(Out-of-Band)
soreceive Function Call • 接收緩衝區的組織架構:訊息邊界
soreceive Function Call • 接收緩衝區的組織架構:沒有訊息邊界
soreceive Function Call • 發出pr_usrreq (PRU_RECV) • tcp_usrreq() • 行程從緩衝區將資料讀走 • 呼叫tcp_output • 判斷是否需要把窗口大小更新資訊傳送出去
select System Call • socket事件
select System Call • selscan Function Call • 找出要監看的descriptor • 登記descriptor • descriptor已經準備好了 • 當一個descriptor已經準備好,相對應的位元就會在輸出descriptor集合中設立 • 當所有descriptor都登記了之後,會放在*retval回傳
select System Call • soo_select Function Call • 每一次soo_select被呼叫只會檢查一個descriptor的狀態 • 如果相關聯的descriptor準備好之後,便會回傳1,若還沒有準備好,則selrecord便會去標記這一個socket的接收或是傳送緩衝區,表示行程正在使用這一個緩衝區,再回傳0 • 檢查socket可否讀取 • 檢查socket可否寫入 • 檢查是否有例外的情形懸宕未決
select System Call • selrecord Function Call • selinfo資料結構 struct selinfo { pid_t si_pid; shout si_flags; } • 這一個資料結構會和每一個接收以及傳送緩衝區儲存在一起(sb_sel) • 當socket緩衝區只有被一個行程select呼叫所指定時,si_pid就是在等待的行程ID • 當有額外的行程在同一個緩衝區上面執行select系統呼叫,SI_COLL=就會在si_flags中設立,稱之為collision
select System Call • 當soo_select呼叫發現還沒準備好descriptor時,selrecord便會被呼叫,會紀錄下足夠的資訊,使這一個緩衝區改變時,這一個行程可以被通訊協定處理層喚醒
select System Call • selwakeup Function Call • 當通訊協定處理改變了socket緩衝區的狀態,且只有一個行程選擇了這一個緩衝區,系統可以根據selinfo中找到的資訊,將行程放到執行佇列中 • 當狀態改變且有超過一個行程選擇了這一個緩衝區時(SI_COLL設立),系統沒有辦法判斷到底哪一些行程會對這一次緩衝區的改變有興趣 • 相對應的wakeup函式將會去喚醒所有被系統停頓的行程,即使對緩衝區活動沒有興趣的行程也一樣
select System Call • 驗證以及準備 • nd: 不可以大於這一個行程相關聯的描述子最大數目 • ni: 用來儲存一個nd位元長的位元遮罩所需要的位元數目 • 從行程複製檔案描述子集合 • 從行程傳輸檔案描述子集合
select System Call • 設定時限值 • 如果tv是一個空指標,timo會被設成0,select會無限期的等待。如果tv不是空指標,時限值就會被複製到系統核心中 • 掃描檔案描述子 • Selscan() • 錯誤或是有一些描述子已經準備好 • 如果有錯誤發生或是有descriptor準備好,就立即返回 • 檢查是否期限到期了 • 如果行程提供了一個時間限制,且目前時間超出了這一個限制值,則立即返回