linux sock结构体,struct socket结构体详解
在內核中為什么要有struct socket結構體呢?
struct socket結構體的作用是什么?
下面這個圖,我覺得可以回答以上兩個問題。?
由這個圖可知,內核中的進程可以通過使用struct socket結構體來訪問linux內核中的網絡系統中的傳輸層、網絡層、數據鏈路層。也可以說struct socket是內核中的進程與內核中的網路系統的橋梁。
struct?socket
{
socket_state??state;?//?socket?state
short???type?;?//?socket?type
unsigned?long??flags;?//?socket?flags
struct?fasync_struct??*fasync_list;
wait_queue_head_t?wait;
struct?file?*file;
struct?sock?*sock;??//?socket在網絡層的表示;
const?struct?proto_ops?*ops;
}
struct?socket結構體的類型
enum?sock_type
{
SOCK_STREAM?=?1,?//?用于與TCP層中的tcp協議數據的struct?socket
SOCK_DGRAM??=?2,?//用于與TCP層中的udp協議數據的struct?socket
SOCK_RAW????=?3,?//?raw?struct?socket
SOCK_RDM????=?4,?//可靠傳輸消息的struct?socket
SOCK_SEQPACKET?=?5,//?sequential?packet?socket
SOCK_DCCP???=?6,
SOCK_PACKET?=?10,?//從dev?level中獲取數據包的socket
};
struct?socket?中的flags字段取值:
#define?SOCK_ASYNC_NOSPACE??0
#define?SOCK_ASYNC_WAITDATA?1
#define?SOCK_NOSPACE????????2
#define?SOCK_PASSCRED???????3
#define?SOCK_PASSSEC????????4
我們知道在TCP層中使用兩個協議:tcp協議和udp協議。而在將TCP層中的數據往下傳輸時,要使用網絡層的協議,而網絡層的協議很多,不同的網絡使用不同的網絡層協議。我們常用的因特網中,網絡層使用的是IPV4和IPV6協議。
所以在內核中的進程在使用struct socket提取內核網絡系統中的數據時,不光要指明struct socket的類型(用于說明是提取TCP層中tcp協議負載的數據,還是udp層負載的數據),還要指明網絡層的協議類型(網絡層的協議用于負載TCP層中的數據)。
linux內核中的網絡系統中的網絡層的協議,在linux中被稱為address family(地址簇,通常以AF_XXX表示)或protocol family(協議簇,通常以PF_XXX表示)。
1.創建一個struct socket結構體:
int sock_create(int family, int type, int protocol,
struct socket **res);
int sock_create_kern(int family, int type, int protocol,
struct socket **res);
EXPROT_SYMBOL(sock_create);
EXPROT_SYMBOL(sock_create_kern);
family : 指定協議簇的類型,其值為:PF_XXX或 AF_XXX
type?? :指定要創建的struct socket結構體的類型;
protocol : 一般為0;
res??? : 中存放創建的struct socket結構體的地址;int?sock_create(int?family,?int?type,?int?protocol,?struct?socket?**res)
{
return?__sock_create(current->nsproxy->net_ns,?family,?type,?protocol,?res,?0);
}
int?sock_create_kern(int?family,?int?type,?int?protocol,?struct?socket?**res)
{
return?__sock_create(?&init_net,?family,?type,?protocot,?res,?1?);
}
如果在內核中創建struct?socket時,推薦使用sock_create_kern()函數;
//?網絡協議簇結構體
struct?net_proto_family
{
int?family?;?//?協議簇
int?(*create)(struct?net?*net,?struct?socket?*sock,??int?protocol);
struct?module??*owner;
};
內核中的所有的網絡協議的響應的網絡協議簇結構體都存放在?net_families[]指針數組中;
static?struct?net_proto_family?*net_families[NPROTO];
static?int?__sock_create(struct?net?*net,?int?family,?int?type,?int?protocol,
struct?socket?**res,?int?kern?)
{
struct?socket?*sock;
struct?net_proto_family?*pf;
sock?=?sock_alloc();//分配一個struct?socket?結構體
sock->type?=?type;
pf?=?rcu_dereference(net_families[family]);?//獲取相應的網絡協議簇結構體的地址;
pf->create(net,?sock,?protocol);?//?對struct?socket結構體做相應的處理;
*res?=?sock;?//?res中保存創建的struct?socket結構體的地址;
return?0;
}
struct?socket_alloc
{
struct?socket?socket?;
struct?inode?vfs_node?;
}
static?inline?struct?socket?*SOCKET_I(struct?inode?*inode)
{
return?&contain_of(inode,?struct?socket_alloc,?vfs->node)->socket;
}
static?struct?socket?*sock_alloc(void)
{
struct?inode?*inode;
struct?socket?*sock;
inode?=?new_inode(sock_mnt->mnt_sb);//分配一個新的struct?inode節點
sock?=?SOCKET_I(inode);
inode->i_mode?=?S_IFSOCK?|?S_IRWXUGO;//設置inode節點的權限
inode->i_uid?=?current_fsuid();?//?設置節點的UID
inode->i_gid?=?current_fsgid();?//設置節點的GID
return?sock;
}
有以上的代碼可知:linux內核在使用sock_create()、sock_create_kern()
進行struct socket結構體的創建時,其本質是分配了一個struct socket_alloc
結構體,而這個struct socket_alloc結構體中包含了struct socket 和struct
inode(struct inode結構體,是linux內核用來刻畫一個存放在內存中的文件的,通過將struct inode 和 struct socket綁定在一起形成struct socket_alloc結構體,來表示內核中的網絡文件)。然后對分配的struct socket結構體進行初始化,來定義內核中的網絡文件的類型(family, type, protocol).
在linux網絡系統中還有兩個非常重要的套接字地址結構體:
struct sockaddr_in
struct sockaddr;typedef?unsigned?short?sa_family_t;
//?Internet?Address
struct?in_addr{
__b32?s_addr;
}
//struct?describing?an?Internet?socket?address
//sockaddr_in?中存放端口號、網路層中的協議類型(ipv4,ipv6)等,網絡層的IP地址;
struct?sockaddr_in
{
sa_family_t?sin_family?;?//?Address?family?AF_XXX
__be16??????sin_port???;?//?端口號
struct?in_addr?sin_addr?;?//?Internet?Address
/*Pad?to?size?of??'struct?sockaddr'*/
...........
};
//套接字地址結構體。
struct?sockaddr
{
sa_family_t?sa_family;?//?存放網絡層所使用的協議類型(AF_XXX?或?PF_XXX);
char?sa_data[14];???//?里面存放端口號、網絡層地址等信息;
}
從本質上來說,struct sockaddr與struct sockaddr_in是相同的。
但在,實際的使用過程中,struct sockaddr_in是 Internet環境下的套接字地址形式,而struct sockaddr是通過的套接字地址個形式。在linux內核中struct sockaddr使用的更多,目的是使linux內核代碼更為通用。
struct sockaddr_in 可以與 struct sockaddr 進行自由的轉換。
2.將創建的套接字(struct socket)與套接字地址結構體(struct sockaddr or struct sockaddr_in)進行綁定:
int kernel_bind(struct socket *sock, struct sockaddr *addr,
int addrlen)
EXPROT_SYMBOL(kernel_bind);
sock : 為通過sock_create()或sock_create_kern()創建的套接字;
addr : 為套接字地址結構體;
addrlen:為套接字地址結構體的大小;
3.將一個套接字(struct socket)設置為監聽狀態:
int kernel_listen(struct socket *sock, int backlog);
backlog :一般情況下設置為0;
EXPORT_SYMBOL(kernel_listen);
4.當把一個套接字設置為監聽狀態以后,使用這個套接字去監聽其它的套接字;
int kernel_accept(struct socket *sock, struct socket **new_sock,
int flags);
EXPORT_SYMBOL(kernel_accept);
sock : listening socket 處于監聽狀態的套接字;
new_sock : 被監聽的套接字;
flags: struct socket中的flags字段的取值;
5.把一個套接字連接到另一個套接字地址結構體上:
int kernel_connect(struc socket *sock, struct sockaddr *addr,
int addrlen, int flags);
EXPORT_SYMBOL(kernel_connect);
sock : struct socket;
addr : 為另一個新的套接字地址結構體;
addrlen : 套接字地址結構體的大小;
flags :file-related flags associated with socket
6.把一個應用層中的數據發送給另一個設備中的進程:
int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
struct kvec *vec, size_t num, size_t size)
EXPORT_SYMBOL(kernel_sendmsg);
sock : 為當前進程中的struct socket套接字;
msg? : 用于接收來自應用層的數據包;
kvec : 中存放將要發送出去的數據;
num? : 見代碼;
size : 為將要發送的數據的長度;
struct?iovec
{
void?__user?*iov_base;
__kernel_size_t?iov_len;
}
struct?msghdr
{
//用于存放目的進程所使用的套接字地址
void?*msg_name;??//?用于存放目的進程的struct?sockaddr_in
int???msg_namelen;?//?目的進程的sizeof(struct?sockaddr_in)
//用于來自應用層的數據
struct?iovec?*msg_iov?;//?指向一個struct?iovec的數組,數組中的每個成員表示一個數據塊
__kernel_size_t??msg_iovlen?;?//數據塊數,即struct?iovec數組的大小
//用于存放一些控制信息
void?*msg_control?;
__kernel_size_t?msg_controllen;?//控制信息的長度;
//
int?msg_flags;
}
struct?kvec
{
void?*iov_base;?//用于存放來自應用層的數據;
size_t?iov_len;?//來自應用層的數據的長度;
}
struct msghdr中的flags字段的取值為:
int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
struct kvec *vec, size_t num, size_t size)函數的實現為:
有kernel_sendmsg()的實現代碼可知,struct kvec中的數據部分最終還是要放到struct msghdr之中去的。
kernel_sendmsg()的用法:
也可以使用下面這個函數來實現相同的功能:
int sock_sendmsg(struct socket *sock, struct msghdr *msg,
size_t size);
EXPORT_SYMBOL(sock_sendmsg);
7.接受來自另一個網絡進程中的數據:
int kernel_recvmsg(struct socket *sock, struct msghdr *msg,
struct kvec *vec, size_t num, size_t size, int flags)
EXPORT_SYMBOL(kernel_recvmsg);
sock : 為接受進程的套接字;
msg? : 用于存放接受到的數據;
vec? : 用于指向本地進程中的緩存區;
num? : 為數據塊的塊數;
size : 緩存區的大小;
flags: struct msghdr中的flags字段中的取值范圍;
int kernel_recvmsg()的實現:
kernel_recvmsg()的用法:
8.關閉一個套接字:
void sock_release(struct socket *sock);
用于關閉一個套接字,并且如果一個它struct socket綁定到了一個struct
inode節點上的話,相應的struct inode也會被釋放。
以上這些函數位于linux源代碼包中的/net/socket.c之中。
總結
以上是生活随笔為你收集整理的linux sock结构体,struct socket结构体详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux动态分配全局置换,深入理解计算
- 下一篇: linux发邮件安装什么意思,linux