日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

linux 协议栈 位置,[置顶] Linux协议栈代码阅读笔记(一)

發布時間:2025/3/19 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 协议栈 位置,[置顶] Linux协议栈代码阅读笔记(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Linux協議棧代碼閱讀筆記(一)

(基于linux-2.6.21.7)

(一)用戶態通過諸如下面的C庫函數訪問協議棧服務

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

int bind(int sockfd, const struct sockaddr *addr,? socklen_t addrlen);

int connect(int sockfd, const struct sockaddr *addr,? socklen_t addrlen);

……

(二)上述C庫函數如何與內核交互

C庫代碼準備好相應的工作后(例如,設置系統調用號啦、參數構造啦、棧啦、寄存器設置啦),通過系統調用指令,進入內核態。從內核返回后,C庫函數再做相應的善后工作,然后將結果返回給用戶程序。

這部分代碼,不同架構的處理器,有不同的實現。

可以參考Glibc的源碼。

下面以X86為例,簡要描述一下這個過程。

另外,后續的內容,如無特殊說明,均是針對X86架構。

對于X86架構,一般是通過“int? $0x80”指令進入內核,即觸發128號中斷。

內核中斷向量表的定義如下(源碼文件archi386kernel Traps.c):

struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };

函數trap_init(源碼文件archi386kernel Traps.c)對此表進行了初始化。

其中,對128號中斷的初始化方式為:

set_system_gate(SYSCALL_VECTOR,&system_call);

SYSCALL_VECTOR宏的值為0x80,即128。

因此,128號中斷,即對應中斷向量表的第128個條目,其中斷服務程序為system_call這段代碼。

system_call這段代碼,是用匯編實現的。

其代碼在archi386kernelentry.S中。

這個代碼,主要是根據系統調用號,索引系統調用表中的一個條目進行執行。

(三)內核態如何處理用戶的網絡通訊請求

上一步,C庫發起了系統調用,進入了內核128號中斷,即系統調用軟中斷。

128號中斷處理程序,根據系統調用號,進入系統調用表的相應表目。系統調用表如下,每個表目是一個函數指針。(源碼文件:archi386kernel syscall_table.S)

ENTRY(sys_call_table)

.long sys_restart_syscall?/* 0 - old "setup()" system call, used for restarting */

.long sys_exit

.long sys_fork

.long sys_read

.long sys_write

.long sys_open??/* 5 */

.long sys_close

.long sys_waitpid

.long sys_creat

.long sys_link

.long sys_unlink?/* 10 */

.long sys_ni_syscall?/* old lock syscall holder */

.long sys_statfs

.long sys_fstatfs?/* 100 */

.long sys_ioperm

.long sys_socketcall

.long sys_syslog

.long sys_tee???/* 315 */

.long sys_vmsplice

.long sys_move_pages

.long sys_getcpu

.long sys_epoll_pwait

對于上述的幾個socket庫函數,全部對應同一個系統調用,即102號系統調用,即sys_socketcall函數。

sys_socketcall函數如何處理用戶的socket請求

所有的socket相關的C庫函數,如socket、bind、connect、listen、accept、send、recv、sendto、sendmsg等,全部都屬于同一個系統調用(即102號系統調用),全部由這一個函數處理。

此函數的代大致如下(源碼文件netSocket.c)

long sys_socketcall(int call, unsigned long __user *args)

{

……

switch (call) {

case SYS_SOCKET:

err = sys_socket(a0, a1, a[2]);

break;

case SYS_BIND:

err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);

break;

case SYS_CONNECT:

err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);

break;

case SYS_LISTEN:

err = sys_listen(a0, a1);

break;

case SYS_ACCEPT:

err =

sys_accept(a0, (struct sockaddr __user *)a1,

(int __user *)a[2]);

break;

case SYS_GETSOCKNAME:

err =

sys_getsockname(a0, (struct sockaddr __user *)a1,

(int __user *)a[2]);

break;

case SYS_GETPEERNAME:

err =

sys_getpeername(a0, (struct sockaddr __user *)a1,

(int __user *)a[2]);

break;

case SYS_SOCKETPAIR:

err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);

break;

case SYS_SEND:

err = sys_send(a0, (void __user *)a1, a[2], a[3]);

break;

case SYS_SENDTO:

err = sys_sendto(a0, (void __user *)a1, a[2], a[3],

(struct sockaddr __user *)a[4], a[5]);

break;

case SYS_RECV:

err = sys_recv(a0, (void __user *)a1, a[2], a[3]);

break;

case SYS_RECVFROM:

err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],

(struct sockaddr __user *)a[4],

(int __user *)a[5]);

break;

case SYS_SHUTDOWN:

err = sys_shutdown(a0, a1);

break;

case SYS_SETSOCKOPT:

err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);

break;

case SYS_GETSOCKOPT:

err =

sys_getsockopt(a0, a1, a[2], (char __user *)a[3],

(int __user *)a[4]);

break;

case SYS_SENDMSG:

err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);

break;

case SYS_RECVMSG:

err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);

break;

default:

err = -EINVAL;

break;

}

return err;

}

(四)socket的創建

使用上述C庫函數,第一步當然是使用socket庫函數創建一個socket。后續的操作,則都是針對這一步創建出的socket而進行了。因此,我們先來看看socket的創建。

創建socket,主要就是分配一個struct socket結構變量,并適當的初始化。

這個工作由sys_socket 通過如下形式調用sock_create完成。其中的參數family、type、protocol都是用戶調用socket庫函數時傳入的。

sock_create(family, type, protocol, &sock);

sock_create成功返回后,就創建了一個struct socket結構變量。

后續的數據收發,狀態維護,差不多都基于這個結構變量了。

struct socket結構如下(源碼文件:netSocket.c)

struct socket {

socket_state??state;

unsigned long??flags;

const struct proto_ops?*ops;

struct fasync_struct?*fasync_list;

struct file??*file;

struct sock??*sk;

wait_queue_head_t?wait;

short???type;

};

這個結構的初始化,主要依賴于family, type, protocol這三項信息,查找到相應的協議棧模塊,填充struct socket結構中相應的成員。

這個初始化過程的層次比較深,涉及較多細節。在下也沒有深入閱讀理解。

不過,我們可以簡單看看大的數據結構。

內核中的協議棧,也是按family, type, protocol這三項信息進行了組織。

a)?固定的協議信息(netipv4 Af_inet.c)

Socket的創建,需要根據family, type, protocol確定一個協議。

內核中固定的協議信息,都在inetsw_array中進行了登記。

Static? struct? inet_protosw? inetsw_array[] =

{

{

.type =?????? SOCK_STREAM,

.protocol =?? IPPROTO_TCP,

.prot =?????? &tcp_prot,

.ops =??????? &inet_stream_ops,

.capability = -1,

.no_check =?? 0,

.flags =????? INET_PROTOSW_PERMANENT |

INET_PROTOSW_ICSK,

},

{

.type =?????? SOCK_DGRAM,

.protocol =?? IPPROTO_UDP,

.prot =?????? &udp_prot,

.ops =??????? &inet_dgram_ops,

.capability = -1,

.no_check =?? UDP_CSUM_DEFAULT,

.flags =????? INET_PROTOSW_PERMANENT,

},

{

.type =?????? SOCK_RAW,

.protocol =?? IPPROTO_IP,?/* wild card */

.prot =?????? &raw_prot,

.ops =??????? &inet_sockraw_ops,

.capability = CAP_NET_RAW,

.no_check =?? UDP_CSUM_DEFAULT,

.flags =????? INET_PROTOSW_REUSE,

}

};

數組中的每個元素,對應一個協議。

其中,每個元素的prot成員,指向相應的協議(如TCP、UDP等)提供的proto結構變量,其中含有大量的函數指針,指向相應的函數,這些函數用于實現各種協議的交互、收發、控制等。這樣一來,每一個協議,在這個數組中都能查到了,如何操作使用他們,也都有了相應的信息。

每個協議的ops成員,也指向一個proto_ops結構變量,其中也包含大量函數指針。這些操作,可以認為是包裝后的,更抽象的操作。是更接近用戶的socket操作函數。例如,這些操作函數包括:bind、connect、listen等。

具體包含哪些操作,是由family, type決定的(例如,family=PF_INET,type=SOCK_DGRAM時,則使用sendmsg接收數據)。對于多個協議,即使實現不同,但是如果他們的family, type相同,那么對于用戶來說,操作都是一樣的。

初始化完成后,最終的情況是:

a)??? socket.sk.__sk_common.skc_prot指向具體的協議提供的proto結構變量。內含大量函數,實現具體的協議操作。

b)??? socket. ops 指向相應的proto_ops結構變量,實現各種socket操作。

c)?? 最終的流程是:socket. ops包裝了socket操作,但是socket. ops中的函數是利用socket.sk.__sk_common.skc_prot中的函數完成最終的操作。

最后,inetsw_array中的元素(協議),不是遍歷查找的。他們被按照type分類組織到中inetsw了。Inetsw包含了PF_INET協議族中的全部協議。

Inetsw是個鏈表數組,定義如下(源碼文件netipv4 Af_inet.c)。

static? struct? list_head? inetsw[SOCK_MAX];

(五)使用socket進行收發

上一步已經完成了相關的初始化工作。

后續的建鏈、收發、斷鏈等操作,也還都是由socketcall這一個函數完成的。

有興趣的朋友可以自己研習研習相關的代碼了。

在下對這方面也沒有深入閱讀理解:)

總結

以上是生活随笔為你收集整理的linux 协议栈 位置,[置顶] Linux协议栈代码阅读笔记(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。