struct socket结构体详解
在内核中为什么要有struct socket结构体呢?
struct socket结构体的作用是什么?
下面这个图,我觉得可以回答以上两个问题。 
由这个图可知,内核中的进程可以通过使用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层中的数据)。
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结构体的地址;
|
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 Addressstruct 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
struct socket结构体详解的更多相关文章
- struct termios结构体详解
一.数据成员 termios 函数族提供了一个常规的终端接口,用于控制非同步通信端口. 这个结构包含了至少下列成员:tcflag_t c_iflag; /* 输入模式 */tcflag_t ...
- 【USB】struct usb_device_id 结构体详解
struct usb_device_id { /* which fields to match against? */ __u16 match_flags; //说明使用哪种匹配方式 /* Used ...
- NetBios 的结构体详解
[NetBios 的结构体详解] NetBIOS是早期的局域网传输协议. 1.结构体. 2.命令 NetBIOS命令的使用方式有两种,即等待和非等待(或称为同步与异步)方式. 如果命令码的高阶位是0时 ...
- struct net_device网络设备结构体详解
转自:http://blog.csdn.net/viewsky11/article/details/53046787 在linux中使用struct net_device结构体来描述每一个网络设备.同 ...
- Linux下DIR,dirent,stat等结构体详解
摘自:http://www.liweifan.com/2012/05/13/linux-system-function-files-operation/ 最近在看Linux下文件操作相关章节,遇到了这 ...
- Linux进程描述符task_struct结构体详解--Linux进程的管理与调度(一)【转】
Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息.它定义在include/linux/sched.h文件中. 谈到task_str ...
- Linux下DIR,dirent,stat等结构体详解(转)
最近在看Linux下文件操作相关章节,遇到了这么几个结构体,被搞的晕乎乎的,今日有空,仔细研究了一下,受益匪浅. 首先说说DIR这一结构体,以下为DIR结构体的定义: struct __dirstre ...
- I2C初始化结构体详解
typedef struct { uint32_t I2C_ClockSpeed; /*设置SCL时钟频,此值不低于40000*/ uint16_t I2C_Mode; /* 指定工作模式,可选I2C ...
- CEF CefSettings 结构体 详解
1. single_process: 设置为ture时,browser和render使用同一个进程.Chromium 不正是支持此运行模式,并且不如默认的多进程稳定. 2. no_sandbox: 沙 ...
随机推荐
- C语言|博客作业09
这个作业属于哪个课程 C语言程序设计II 这个作业的要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-1/homework/10027 我在这个课程 ...
- [转发]CSR 量产 烧录 软件
蓝牙量产软件主要是为了应对蓝牙设备在批量生产时的一些如固件下载,地址下载,名字修改,以及一些辅助测试和检验功能. 目前,CSR推出的蓝牙芯片按照存储介质以及可编程与否分为两大类:ROM版本和Flash ...
- 小白学习React官方文档看不懂怎么办?2.JSX语法
接下来我们就要讲到JSX语法了,在我们讲它之前,我们先引入一个概念叫语法糖. 听到这个名字首先我们可能会想到一个词叫”糖衣炮弹“,那么什么叫糖衣炮弹呢,就是给你说各种好听的话,来迷惑你,但 ...
- 检测当前IE浏览器的版本
检测当前IE浏览器的版本(注意:在非IE浏览器中是看不到效果的) 使用示例如下:低于IE8弹窗提示 <!--[if lte IE 8]><script>alert('您当前浏览 ...
- Jib插件构建镜像push到阿里云镜像仓库
一.前言 Jib:Google开源的Java容器化工具 可作为插件快速集成到项目中,构建镜像,实现 Java 应用容器化 下面贴出一张从网上看到的Jib描述~ 二.利用Jib插件构建镜像push到阿里 ...
- 【集合系列】- 深入浅出的分析 WeakHashMap
一.摘要 在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap.LinkedHashMap.TreeMap.IdentityHashMap.WeakHashMap.Hashtable.P ...
- ArcGIS 切片与矢量图图层顺序问题
在项目中有个需求:根据图层索引添加图层 看到这个需求一下子想到 map.addLayer(layer,index?) 接口 但是问题出现了,我切片图加载顺序在矢量图之后就不行! map = new M ...
- Fragment的生命周期(与Activity的对比)
Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期.官网这张图很好的说明了两者生命周期的关系: 可以看到Fragment比Act ...
- 小程序api的promise封装
微信小程序和支付宝小程序的api封装方法是一样的,都是外部新建一个js,使用module.exports导出,要注意的是,最好使用post请求,虽然get请求没什么不好,主要是好修改.这里使用的MD5 ...
- 【论文阅读】Learning Dual Convolutional Neural Networks for Low-Level Vision
论文阅读([CVPR2018]Jinshan Pan - Learning Dual Convolutional Neural Networks for Low-Level Vision) 本文针对低 ...