在內核中爲何要有struct socket結構體呢?node
struct socket結構體的做用是什麼?linux
由這個圖可知,內核中的進程能夠經過使用struct socket結構體來訪問linux內核中的網絡系統中的傳輸層、網絡層、數據鏈路層。也能夠說struct socket是內核中的進程與內核中的網路系統的橋樑。緩存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
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層中的數據)。socket
linux內核中的網絡系統中的網絡層的協議,在linux中被稱爲address family(地址簇,一般以AF_XXX表示)或protocol family(協議簇,一般以PF_XXX表示)。async
1.建立一個struct socket結構體:tcp
int sock_create(int family, int type, int protocol,
函數
struct socket **res);ui
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結構體的地址;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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 : 爲將要發送的數據的長度;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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;
}
|
1
2
3
4
5
|
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之中。
本文出自 「阿輝仔」 博客,請務必保留此出處http://weiguozhihui.blog.51cto.com/3060615/1585297