網(wǎng)絡(luò)實(shí)現(xiàn)架構(gòu)

4.4BSD通過同時(shí)對(duì)多種通信協(xié)議的支持來提供通用的底層基礎(chǔ)服務(wù)。4.4BSD支持四種不同的通信協(xié)議簇:

  • TCP/IP(互聯(lián)網(wǎng)協(xié)議簇)

  • XNS(Xerox網(wǎng)絡(luò)系統(tǒng))

  • OSI協(xié)議

  • Unix域協(xié)議
    從通信協(xié)議是用來在不同的系統(tǒng)之間交換信息的意義上來說,它還不算是一套真正的協(xié)議,但它提供了一種進(jìn)程間通信(IPC)的形式。

4.4BSD內(nèi)核中的聯(lián)網(wǎng)代碼組織成三層,如下圖所示

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

  • Socket層是一個(gè)到下面協(xié)議相關(guān)層的協(xié)議無關(guān)層。所有系統(tǒng)調(diào)用從協(xié)議無關(guān)的Socket開始
    例如:在Socket層中的bind()系統(tǒng)調(diào)用的協(xié)議無關(guān)代碼包含幾十行代碼,它們驗(yàn)證第一個(gè)參數(shù)是一個(gè)有效的socket描述符,并且第二個(gè)參數(shù)是一個(gè)進(jìn)程中的有效指針。然后調(diào)用下層的協(xié)議相關(guān)代碼,協(xié)議相關(guān)代碼可能包含幾百行代碼。

  • 協(xié)議層包括我們提到的四種協(xié)議簇(TCP/IP,XNS,OSI和Unix域)的實(shí)現(xiàn)。
    每個(gè)協(xié)議簇可能包含自己的內(nèi)部結(jié)構(gòu)。

  • 接口層
    接口層包括同網(wǎng)絡(luò)設(shè)備通信的設(shè)備驅(qū)動(dòng)程序。

數(shù)據(jù)傳遞

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

  • Socket層中的每一個(gè)Socket都具有一個(gè)輸入隊(duì)列和一個(gè)輸出隊(duì)列

  • 協(xié)議層中的每一個(gè)協(xié)議都具有一個(gè)輸入隊(duì)列和輸出隊(duì)列

  • 接口層中的每個(gè)接口(以太網(wǎng)、回環(huán)、SLIP、PPP等)都有一個(gè)輸入隊(duì)列和輸出隊(duì)列

輸入處理

輸入處理與輸出處理不同,因?yàn)檩斎胩幚硎钱惒降摹>褪钦f,它是通過一個(gè)接收完成中斷驅(qū)動(dòng)以太網(wǎng)設(shè)備程序來接收一個(gè)輸入分組,而不是通過進(jìn)程的系統(tǒng)調(diào)用。內(nèi)核處理這個(gè)設(shè)備中斷,并調(diào)度設(shè)備驅(qū)動(dòng)程序進(jìn)入運(yùn)行狀態(tài)。

接口層-以太網(wǎng)輸入

以太網(wǎng)設(shè)備驅(qū)動(dòng)程序處理這個(gè)中斷。

假定它表示一個(gè)正常的接收已完成,數(shù)據(jù)從以太網(wǎng)設(shè)備讀取到一個(gè)mbuf鏈表中。設(shè)備驅(qū)動(dòng)程序把mbuf傳給一個(gè)通用以太網(wǎng)輸入例程,它通過以太網(wǎng)幀中的類型字段來確定哪個(gè)協(xié)議層接收此分組。

協(xié)議層——IP輸入

IP輸入是異步的,并且通過一個(gè)軟中斷來執(zhí)行。

當(dāng)接口層在系統(tǒng)的一個(gè)接口上收到一個(gè)IP數(shù)據(jù)報(bào)時(shí),它就設(shè)置這個(gè)軟中斷。當(dāng)IP輸入例程執(zhí)行它時(shí),循環(huán)處理在它的輸入隊(duì)列中的每一個(gè)IP數(shù)據(jù)報(bào),并在整個(gè)隊(duì)列被處理完后返回。

輸入層-UDP輸入

IP輸入歷程可能會(huì)調(diào)用UDP輸入例程去處理UDP數(shù)據(jù)報(bào)。

UDP輸入例程驗(yàn)證UDP首部中的各字段(長度與可選的校驗(yàn)和),然后確定是否一個(gè)進(jìn)程應(yīng)噶接收次數(shù)據(jù)報(bào)。

UDP輸入例程從一個(gè)全局變量udb開始,查看所有UDP協(xié)議控制塊鏈表PCB,尋找一個(gè)本地端口號(hào)與接收的UDP數(shù)據(jù)報(bào)的目標(biāo)端口號(hào)相匹配的協(xié)議塊。(這個(gè)PCB是由我們調(diào)用socket()創(chuàng)建的,它的成員inp_socket指向相應(yīng)socket接收,并允許接收的數(shù)據(jù)在此socket排隊(duì)).

因?yàn)檫@個(gè)UDP數(shù)據(jù)報(bào)要傳送給我們的進(jìn)程,發(fā)送方的IP地址和UDP端口號(hào)放置到一個(gè)mbuf中,這個(gè)mbuf和數(shù)據(jù)被追加到此socket的接收隊(duì)列中。

最后,接收進(jìn)程被喚醒。如果進(jìn)程處于睡眠狀態(tài)等待數(shù)據(jù)的到達(dá),進(jìn)程將標(biāo)志為可運(yùn)行狀態(tài)等待內(nèi)核的調(diào)度。也可以通過select系統(tǒng)調(diào)用或SIGIO信號(hào)來通知進(jìn)程數(shù)據(jù)的到達(dá)。

進(jìn)程輸入

進(jìn)程可以調(diào)用socket 的輸入函數(shù)將mbuf從socket的接收隊(duì)列復(fù)制到我們程序的緩存中。

存儲(chǔ)器緩存

在BSD聯(lián)網(wǎng)代碼設(shè)計(jì)中的一個(gè)基本概念就是存儲(chǔ)器緩存,稱作為一個(gè)mbuf(memory buffer),在整個(gè)聯(lián)網(wǎng)代碼中用于存儲(chǔ)各種信息。

網(wǎng)絡(luò)協(xié)議對(duì)內(nèi)核的存儲(chǔ)器管理能力提出了很多要求。這些要求包括能方便地操作可變長緩存,能在緩存頭部和尾部添加數(shù)據(jù)(如底層封裝來自高層的數(shù)據(jù)),能從緩存中移去數(shù)據(jù)(如,當(dāng)數(shù)據(jù)分組向上經(jīng)過協(xié)議棧時(shí)要去掉首部),并盡量減少為這些操作所做的數(shù)據(jù)復(fù)制。內(nèi)核中的存儲(chǔ)器管理調(diào)度直接關(guān)系到聯(lián)網(wǎng)協(xié)議的性能。

mbuf的主要用途是保存在進(jìn)程和網(wǎng)絡(luò)接口間互相傳遞的用戶數(shù)據(jù)。但mbuf也用于保存其它各種數(shù)據(jù):源與目的地址、Socket選項(xiàng)等等。

  • 指針m_nextmbuf連接在一起,把一個(gè)分組形成一條mbuf鏈表。

  • 指針m_nextpkt把多個(gè)分組鏈接成一個(gè)mbuf鏈接成一個(gè)mbuf鏈表隊(duì)列。在隊(duì)列的每個(gè)分組可以是一個(gè)單獨(dú)的mbuf,也可以是一個(gè)mbuf鏈表。每個(gè)分組的第一個(gè)mbuf包含一個(gè)分組首部。如果多個(gè)mbuf定義一個(gè)分組,只有第一個(gè)mbuf的成員m_nextpkt被使用——鏈表中其它mbuf的成員m_nextpkt全是空指針。

m_get函數(shù)

struct mbuf * m_get(int nowait,int type){    struct mbuf * m;
    MGET(m,nowait,type);    return m;
}
  • nowait的值為M_WAITM_DONTWAIT,它取決于在存儲(chǔ)器不可用時(shí)是否要求等待。
    例如,當(dāng)Socket層請(qǐng)求分配一個(gè)mbuf來存儲(chǔ)sendto系統(tǒng)調(diào)用的目的地址時(shí),它指定M_WAIT,因?yàn)樵诖俗枞菦]有問題的。但是當(dāng)以太網(wǎng)設(shè)備驅(qū)動(dòng)程序請(qǐng)求分配一個(gè)mbuf來存儲(chǔ)一個(gè)接收的幀時(shí),它指定M_DONTWAIT,因?yàn)樗亲鳛橐粋€(gè)設(shè)備中斷處理來執(zhí)行的,不能進(jìn)入睡眠狀態(tài)來等待一個(gè)mbuf。在這種情況下,若存儲(chǔ)器不可用,設(shè)備驅(qū)動(dòng)程序丟棄這個(gè)幀比較好。

  • type 指定mbuf的類型

系統(tǒng)調(diào)用

所有的操作系統(tǒng)都提供服務(wù)訪問點(diǎn),程序可以通過它們請(qǐng)求內(nèi)核中的服務(wù)。各種UNIX都提供精心定義的有限個(gè)內(nèi)核入口點(diǎn),即系統(tǒng)調(diào)用。我們不能改變系統(tǒng)調(diào)用,除非我們有內(nèi)核的源代碼。

在各種Unix系統(tǒng)中,每個(gè)系統(tǒng)調(diào)用在標(biāo)準(zhǔn)C函數(shù)庫中都有一個(gè)相同名字的函數(shù)。一個(gè)應(yīng)用程序用標(biāo)準(zhǔn)C的調(diào)用序列來調(diào)用此函數(shù)。這個(gè)函數(shù)再調(diào)用相應(yīng)的內(nèi)核服務(wù),所使用的技術(shù)依賴于所在的系統(tǒng)。例如,函數(shù)可能把一個(gè)或多個(gè)C參數(shù)放到通用寄存器中,并執(zhí)行幾條機(jī)器指令產(chǎn)生一個(gè)軟件中斷進(jìn)入內(nèi)核。對(duì)我們來說,我們可以把系統(tǒng)調(diào)用看成C函數(shù)。

從進(jìn)程到內(nèi)核的受保護(hù)的環(huán)境的轉(zhuǎn)換是與機(jī)器和實(shí)現(xiàn)相關(guān)的。

BSD內(nèi)核中,每一個(gè)系統(tǒng)調(diào)用均被編號(hào),當(dāng)進(jìn)程執(zhí)行一個(gè)系統(tǒng)調(diào)用時(shí),硬件被配置成僅傳送控制給一個(gè)內(nèi)核函數(shù),即將CPU的使用權(quán)轉(zhuǎn)給一個(gè)內(nèi)核函數(shù)。將標(biāo)志系統(tǒng)調(diào)用的整數(shù)作為參數(shù)傳送給此內(nèi)核函數(shù)。在i386實(shí)現(xiàn)中,此內(nèi)核函數(shù)為syscall(),syscall()利用系統(tǒng)調(diào)用的編號(hào)在系統(tǒng)調(diào)用表中找到請(qǐng)求的系統(tǒng)調(diào)用的sysent結(jié)構(gòu).表中的每一單元均為一個(gè)sysent結(jié)構(gòu)。

struct sysent{    int sy_narg;  //參數(shù)個(gè)數(shù)
    int (*sy_call)();//系統(tǒng)調(diào)用的實(shí)現(xiàn)函數(shù)};

表中有幾個(gè)項(xiàng)是從sysent數(shù)據(jù)中來的,概述組是在kern/init_sysent.c中定義的:

struct sysent sysent[] = {
  {3,recvmsg},    /* 27 = recvmsg */
  {3,sendmsg},    /* 28 = sendmsg */
  {6,recvfrom},   /* 29 = recvfrom */
  {3,accept},     /* 30 = accept */
  {3,getpeername},/* 31 = getpeername */
  {3,getsockname},/* 32 = getsockname */};

例如,recvmsg系統(tǒng)調(diào)用在系統(tǒng)調(diào)用表中的第27個(gè)項(xiàng),它有2個(gè)參數(shù),利用內(nèi)核中的recvmsg函數(shù)實(shí)現(xiàn)。

syscall()負(fù)責(zé)將參數(shù)從調(diào)用進(jìn)程復(fù)制到內(nèi)核中,并且分配一個(gè)數(shù)組來保存系統(tǒng)調(diào)用的結(jié)果。然后,當(dāng)系統(tǒng)調(diào)用執(zhí)行完成后,syscall將結(jié)果返回給進(jìn)程。syscall將控制交給魚系統(tǒng)調(diào)用相對(duì)應(yīng)的內(nèi)核函數(shù)。

在i386實(shí)現(xiàn)中,調(diào)用有點(diǎn)像:

struct sysent * callp;
error = (*callp->syscall)(p,args,rval);if(error){
    errno = error;    return -1;
}else{    return (rval);
}

這里指針callp指向相關(guān)的sysent結(jié)構(gòu);指針p指向調(diào)用系統(tǒng)調(diào)用的進(jìn)程的進(jìn)程表項(xiàng);args作為參數(shù)傳給系統(tǒng)調(diào)用,它是一個(gè)32bit長的字?jǐn)?shù)組;而rval則是一個(gè)用來保存系統(tǒng)調(diào)用的返回結(jié)果的數(shù)組,數(shù)組有兩個(gè)元素,每一個(gè)元素是一個(gè)32bit長的字。當(dāng)我們用"系統(tǒng)調(diào)用"這個(gè)詞時(shí),我們指的是被syscall調(diào)用的內(nèi)核中的函數(shù),而是不是應(yīng)用調(diào)用的進(jìn)程中的函數(shù)

syscall期望系統(tǒng)調(diào)用函數(shù)(即sy_call指向的函數(shù))在沒有差錯(cuò)時(shí)返回0,否則返回非0的差錯(cuò)代碼。如果沒有差錯(cuò)出現(xiàn),內(nèi)核將rval中的值作為系統(tǒng)調(diào)用(應(yīng)用調(diào)用的)返回值傳送給進(jìn)程。如果有差錯(cuò),syscall忽略rval中的值,并以與機(jī)器相關(guān)的方式返回差錯(cuò)代碼給進(jìn)程,使得進(jìn)程能從外部變量errno中得到差錯(cuò)代碼。應(yīng)用調(diào)用的函數(shù)則返回-1或一個(gè)空指針表示應(yīng)用應(yīng)該查看errno獲得差錯(cuò)信息。

下表介紹了與網(wǎng)絡(luò)有關(guān)的系統(tǒng)調(diào)用

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)
大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)
大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

舉例

socket系統(tǒng)調(diào)用的函數(shù)原型是:

int socket(int domain,int type,int protocol);

實(shí)現(xiàn)socket系統(tǒng)調(diào)用的內(nèi)核函數(shù)原型是:

struct socket_args{  int domain;  int type;  int protocl;
};

socket(struct proc * p,struct socket_args * uap,int * retvall);

當(dāng)一個(gè)應(yīng)用調(diào)用socket時(shí),進(jìn)程用系統(tǒng)調(diào)用機(jī)制將三個(gè)獨(dú)立的整數(shù)傳給內(nèi)核。syscall將參數(shù)復(fù)制到32bit值的數(shù)組中,并將數(shù)組指針作為第二個(gè)參數(shù)傳給socket的內(nèi)核版。內(nèi)核版的socket將第二個(gè)參數(shù)作為指向socket_args結(jié)構(gòu)的指針。下圖描述了上述過程:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

同socket類似,(在i386實(shí)現(xiàn)中)每一個(gè)實(shí)現(xiàn)系統(tǒng)調(diào)用的內(nèi)核函數(shù)將args說明稱一個(gè)與系統(tǒng)調(diào)用有關(guān)的結(jié)構(gòu)指針,而不是一個(gè)指向32bit的子的數(shù)組的指針。

syscall在執(zhí)行內(nèi)核系統(tǒng)調(diào)用函數(shù)之前將返回值設(shè)置為0.如果沒有差錯(cuò)出現(xiàn),系統(tǒng)調(diào)用函數(shù)直接返回而不需要清楚*tetvall,syscall返回0給進(jìn)程。

進(jìn)程、描述符和插口

Unix系統(tǒng)中的Socket I/O遵循其"一切皆文件"的思想,因而可以使用統(tǒng)一的方式對(duì)Socket 進(jìn)行I/O操作。

調(diào)用socket()時(shí)要求定義socket類型。Internet協(xié)議族(PF_INET)和數(shù)據(jù)報(bào)socket(SOCK_DGRAM)組合成一個(gè)UDP協(xié)議socket。

socket()的返回值是一個(gè)文件描述符,它具有其它Unix文件描述符的所有特性:可以用這個(gè)描述符調(diào)用read()write();可以用dup()復(fù)制它,在調(diào)用了fork()之后,父進(jìn)程和子進(jìn)程可以共享它;可以用fcntl()來改變他的屬性,可以調(diào)用close()來關(guān)閉它,等的。

在每個(gè)進(jìn)程的生存期內(nèi)都會(huì)有一個(gè)對(duì)應(yīng)的進(jìn)程表項(xiàng)存在。

一個(gè)文件描述符是進(jìn)程對(duì)應(yīng)的進(jìn)程表項(xiàng)中的一個(gè)數(shù)組的下標(biāo).這個(gè)數(shù)組項(xiàng)是一個(gè)指向打開文件表結(jié)構(gòu)的指針。

此打開文件表結(jié)構(gòu)有指向一個(gè)描述此文件的i-nodev-node結(jié)構(gòu)

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

實(shí)現(xiàn)系統(tǒng)調(diào)用的函數(shù)的第一個(gè)參數(shù)總為p,即指向調(diào)用進(jìn)程的proc結(jié)構(gòu)的指針。內(nèi)核利用proc結(jié)構(gòu)體記錄進(jìn)程的有關(guān)信息。在proc結(jié)構(gòu)體中,p_fd指向filedesc結(jié)構(gòu),該結(jié)構(gòu)的主要功能是管理fd_ofiles指向的描述符表描述符表的大小是動(dòng)態(tài)變化的,由一個(gè)指向file結(jié)構(gòu)的指針數(shù)組組成。每一個(gè)file結(jié)構(gòu)體描述一個(gè)打開的文件,該結(jié)構(gòu)體可被多個(gè)進(jìn)程共享。

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

通過p->p_fd->fd_ofiles[fd]訪問到結(jié)構(gòu)。在file結(jié)構(gòu)中,有兩個(gè)結(jié)構(gòu)成員是我們感興趣的:f_opsf_data。I/O系統(tǒng)調(diào)用(如read和write)的實(shí)現(xiàn)因描述符中的I/O對(duì)象類型的不同而不同。f_ops指向fileops結(jié)構(gòu),該結(jié)構(gòu)包含一張readwrite、ioctlselectclose系統(tǒng)調(diào)用的函數(shù)指針表。顯示f_ops指向一個(gè)全局的fileops結(jié)構(gòu),即socketops,該結(jié)構(gòu)包含指向socket用的函數(shù)的指針。

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

f_data指向相關(guān)I/O對(duì)象的專用數(shù)據(jù)。對(duì)于socket而言,f_data指向與描述符相關(guān)的socket結(jié)構(gòu)。最后,socket結(jié)構(gòu)中的so_proto指向產(chǎn)生socket時(shí)選中的協(xié)議的protosw結(jié)構(gòu)。回想一下,每一個(gè)protosw結(jié)構(gòu)是由與該協(xié)議關(guān)聯(lián)的所有socket共享的。

Socket結(jié)構(gòu)

Socket代表一條通信鏈路的一端,存儲(chǔ)或指向與鏈路有關(guān)的所有信息。這些信息包括:使用的協(xié)議、協(xié)議的狀態(tài)信息(包括源地址和目的地址)、到達(dá)的連接隊(duì)列、數(shù)據(jù)緩存可選標(biāo)識(shí)

struct socket{    short so_type;//Socket類型,SOCK_STREAM、SOCK_DGRAM或SOCK_RAW
    short so_options;//Socket行為的標(biāo)志
    short so_linger;    short so_state;//Socket狀態(tài)
    caddr_t so_pcb;//協(xié)議控制塊(Protocol Control Block)
    struct protosw * sp_proto;//協(xié)議處理函數(shù)


    /**     * Socket連接隊(duì)列相關(guān)     */
    struct socket * so_head;    struct socket * so_qo;    struct socket * so_q;    short so_q0len;    short so_qlen;    short so_qlimit;    short so_timeo;

    u_short so_error;    pid_t so_pgid;
    u_long so_oobmasrk;    /**     * Socket緩存相關(guān)變量     */
    struct sockbuf{        struct mbuf * sb_mb;//mbuf鏈,用于存儲(chǔ)用戶數(shù)據(jù)
        
        u_long sb_cc;//緩存中的實(shí)際字節(jié)數(shù)
        
        u_long sb_hiwat;
        u_long sb_mbcnt;
        u_long sb_mbmax;//分配給此socket mbuf緩存的存儲(chǔ)器數(shù)量的上限。
        long sb_lowat;        struct selinfo sb_sel;        short sb_flags;        
        short sb_timeo;//read/write超時(shí)時(shí)間
    } so_rcv,so_snd; //Socket的輸入緩存和輸出緩存
    
    caddr_t so_tpcb;    void (*so_upcall)(struct socket * so,caddr_t arg,int waitf);    caddr_t so_upcallarg;
};

通用字段

so_type

so_type由產(chǎn)生Socket的進(jìn)程來指定,它指明Socket和相關(guān)協(xié)議支持的通信語義。

pr_type協(xié)議語義Internet協(xié)議
SOCK_STREAM可靠的雙向字節(jié)流服務(wù)TCP
SOCK_DGRAM最好的傳輸層數(shù)據(jù)報(bào)服務(wù)UDP
SOCK_RAW最好的網(wǎng)絡(luò)層數(shù)據(jù)報(bào)服務(wù)ICMP、IGMP、原始IP
SOCK_RDM可靠的數(shù)據(jù)報(bào)服務(wù)(未實(shí)現(xiàn))??
SOCK_SEQPACKET可靠的雙向記錄流服務(wù)??

對(duì)于UDP,so_type等于SOCK_DGRAM,而對(duì)于TCP,so_type等于SOCK_STREAM

so_options

so_options是一組改變Socket行為的標(biāo)志。

| so_options | 描述 |
| SO_ACCEPTCONN | Socket接收進(jìn)入的連接(僅用于內(nèi)核) |
| SO_BROADCAST | Socket能夠發(fā)送廣播報(bào)文 |
| SO_DEBUG | Socket記錄排錯(cuò)信息 |
| SO_DONTROUTE | 輸出操作旁路選路表 |
| SO_KEEPALIVE | Socket查詢空閑的連接 |
| SO_OOBINLINE | Socket將帶外數(shù)據(jù)同正常數(shù)據(jù)存放在一起 |
| SO_REUSEADDR | Socket能重新使用一個(gè)本地地址 |
| SO_REUSEPORT | Socket能重新使用一個(gè)本地地址和端口 |
| SO_USELOOPBACK | 僅針對(duì)選路域Socket,發(fā)送進(jìn)程收到它自己的選路請(qǐng)求 |

so_linger

so_linger表示當(dāng)關(guān)閉一條連接時(shí)Socket繼續(xù)發(fā)送數(shù)據(jù)的時(shí)間間隔(單位為一個(gè)時(shí)鐘滴答)。

so_state

so_state表示Socket的內(nèi)部狀態(tài)和一些其它的特點(diǎn)。

so_state描述
SS_NBIOSocket操作不能阻塞進(jìn)程
SS_ASYNCSocket應(yīng)該I/O事件的異步通知

SS_NBIO

默認(rèn)情況下,進(jìn)程在發(fā)出I/O請(qǐng)求后會(huì)等待資源,并阻塞。
例如,當(dāng)一個(gè)進(jìn)程對(duì)一個(gè)Socket進(jìn)行read()系統(tǒng)調(diào)用,如果當(dāng)前沒有網(wǎng)絡(luò)上來的數(shù)據(jù),則read系統(tǒng)調(diào)用就會(huì)被阻塞。同樣,當(dāng)一個(gè)進(jìn)程對(duì)一個(gè)Socket進(jìn)行write()系統(tǒng)調(diào)用,如果內(nèi)核中沒有緩存來存儲(chǔ)發(fā)送的數(shù)據(jù),則內(nèi)核將阻塞進(jìn)程。

如果設(shè)置了SS_NBIO,在對(duì)Socket執(zhí)行I/O操作且請(qǐng)求的資源不能得到時(shí),內(nèi)核并不阻塞進(jìn)程,而是返回EWOULDBLOCK.

SS_ASYNC和so_pgid字段

如果設(shè)置了SS_ASYNC,當(dāng)因?yàn)橄铝星闆r之一而使Socket狀態(tài)發(fā)生變化時(shí),內(nèi)核發(fā)送SIGIO信號(hào)給so_pgid字段標(biāo)識(shí)的進(jìn)程或進(jìn)程組:

  • 連接請(qǐng)求已經(jīng)完成

  • 斷開連接請(qǐng)求已被啟動(dòng)

  • 斷開連接請(qǐng)求已經(jīng)完成

  • 連接的一個(gè)通道已被關(guān)閉

  • Socket上有數(shù)據(jù)到達(dá)

  • 數(shù)據(jù)已被發(fā)送(即輸出緩存中有空閑空間)

  • UDP或TCP Socket上出現(xiàn)了一個(gè)異步差錯(cuò)

so_pcb和協(xié)議控制塊

so_pcb指向協(xié)議控制塊,協(xié)議控制塊包含與協(xié)議有關(guān)的狀態(tài)信息和Socket參數(shù)。
每一種協(xié)議都定義了自己的協(xié)議控制塊結(jié)構(gòu),因此so_pcb被定義成一個(gè)通用的指針。

協(xié)議協(xié)議控制塊
UDPstruct inpcb
TCPstruct inpcb、struct tcpcb
ICMP、IGMP和原始IPstruct inpcb
路由struct rawcb

so_proto

so_proto指向進(jìn)程在socket()系統(tǒng)調(diào)用中選擇的協(xié)議的protosw結(jié)構(gòu)

連接隊(duì)列

在so_options字段中設(shè)置了SO_ACCEPTCONN標(biāo)志的socket維護(hù)兩個(gè)連接隊(duì)列。

so_q0表示還沒有完全建立的連接,例如TCP的三次握手還沒有完成。隊(duì)列的長度由so_q0len字段表示

so_q表示已經(jīng)建立的,但未被應(yīng)用層接受的連接,例如TCP的三次握手已經(jīng)完成。隊(duì)列的長度由so_qlen字段表示。

在每一個(gè)被排隊(duì)的socket中,so+heade指向了設(shè)置SO_ACCEPTCONN的源socket.

socket上可排隊(duì)的連接數(shù)(so_q和so_q0兩個(gè)連接隊(duì)列的總連接數(shù))通過so_qlimit來控制,應(yīng)用層可以通過listen()系統(tǒng)調(diào)用來設(shè)置so_qlimit

當(dāng)下列的不等式成立時(shí),將不再接受任何連接

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

so_timeo

so_timeo用作accept()、connect()close()處理期間的等待通道

等待通道

so_error

so_error保存錯(cuò)誤代碼,直到在應(yīng)引用該socket的下一個(gè)系統(tǒng)調(diào)用期間錯(cuò)誤碼能送給應(yīng)用層

數(shù)據(jù)緩存

每一個(gè)socket包括兩個(gè)數(shù)據(jù)緩存,輸入緩存so_rcv和輸出緩存so_snd。分別用來緩存接受或發(fā)送的數(shù)據(jù)。

socket系統(tǒng)調(diào)用

socket系統(tǒng)調(diào)用產(chǎn)生一個(gè)新的socket,并將socket同進(jìn)程在參數(shù)domaintypeprotocol中指定的協(xié)議聯(lián)系起來。該函數(shù)分配一個(gè)新的描述符,用來在后續(xù)的系統(tǒng)調(diào)用中標(biāo)志socket,并將描述符返回給進(jìn)程。

struct socket_args {  int domain;  int type;  int protocol;
};

socket(struct proc * p,struct socket_args * uap,int * retval)
{  struct filedesc * fdp = p->p_fd;//獲取文件描述符
  struct socket * so;  struct file * fp;  int fd,error;  
  
  if (error = falloc(p,&fp,&fd)){      return (error);
  };
  fp->f_flag = FREAD | FWRITE;
  fp->f_type = DTYPE_SOCKET;
  fp->f_ops = &socketops;  if(error = socreate(uap->domain,&so,uap->type,uap->protocol)){
      fdp->fd_ofiles[fd] = 0;
      ffree(fp);
  }else{
      fp->f_data = (caddr_t)so;
      *retval = fd;
  };  
  return error;
};

falloc分配一個(gè)新的file結(jié)構(gòu)和fd_ofiles數(shù)組中的一個(gè)元素。fp指向新分配的結(jié)構(gòu),fd則為結(jié)構(gòu)在數(shù)組fd_ofiles中的索引。socket將file結(jié)構(gòu)設(shè)置成可讀、可寫,并且作為一個(gè)socket。將所有socket共享的全局fileops結(jié)構(gòu)socketopts連接到f_ops指向的file結(jié)構(gòu)中。socketops變量在編譯時(shí)被初始化。

getsock函數(shù)

getsock的功能是將一個(gè)文件描述符映射到一個(gè)文件表項(xiàng)中,即根據(jù)一個(gè)文件描述符找到起對(duì)應(yīng)的文件表項(xiàng)。。
getsock函數(shù)利用fdp查找文件描述符fdes指定的文件表項(xiàng),fdp是指向filedesc結(jié)構(gòu)的指針。getsock將打開的文件結(jié)構(gòu)指針賦給fpp,并返回,或者當(dāng)出現(xiàn)下列情況時(shí)返回錯(cuò)誤代碼:

  • 描述符的值超過了范圍而不是指向一個(gè)打開的文件

  • 描述符沒有同socket建立聯(lián)系

getsock(struct filedesc * fdp,int fdes,struct file **fpp)
{    struct file * fp;    
    //文件描述符的值超過了范圍。                文件描述符不存在
    if((unsigned) fdes >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fdes] == NULL)){        return (EBADF);
    }    
    //文件描述符指向的不是socket
    if(fp->f_type != DTYPE_SOCKET){        return (ENOTSOCK);
    }
    
    *fp = fp;    return (0);
};

sockargs函數(shù)

sockargs將進(jìn)程傳入的參數(shù)復(fù)制到內(nèi)核中的一個(gè)新分配的mbuf中。

sockargs將進(jìn)程傳給系統(tǒng)調(diào)用的參數(shù)的指針從進(jìn)程復(fù)制到內(nèi)核而不是復(fù)制指向的數(shù)據(jù),這樣做是因?yàn)?strong style="margin: 0px; padding: 0px;">每一個(gè)參數(shù)的語義只有相對(duì)應(yīng)的系統(tǒng)調(diào)用才知道,而不是針對(duì)所有的系統(tǒng)調(diào)用。多個(gè)系統(tǒng)調(diào)用在調(diào)用sockargs復(fù)制參數(shù)指針后,將指針指向的數(shù)據(jù)從進(jìn)程復(fù)制到內(nèi)核中新分配的mbuf中。

例如,sockargs將bind的第二個(gè)參數(shù)指向的本地socket地址從進(jìn)程復(fù)制到一個(gè)mbuf中。

sockargs(struct mbuf **mp,caddr_t buf,int buflen,int type)
{    struct sockaddr * sa;    struct mbuf * m;    int error;    
    if((u_int)buflen > MLEN){        return (EINVAL);
    }
    http://www.cnblogs.com/kakawater/p/7085122.html