Linux内核网络协议栈4-创建socket(2)
接上篇“创建socket” 一文;
5、分配sock结构:
本文中的例子会调用inet_family_ops.create方法即inet_create方法完成socket的创建工作;其调用链如下:
net/Socket.c:sys_socket()->sock_create()->__sock_create()->net/ipv4/Af_inet.c:inet_create();
inet_create()主要完成以下几个工作:
1) 设置socket的状态为SS_UNCONNECTED;
- sock->state = SS_UNCONNECTED;
2) 根据socket的type找到对应的套接字类型:
- list_for_each_rcu(p, &inetsw[sock->type]) {
- answer = list_entry(p, struct inet_protosw, list);
- /* Check the non-wild match. */
- if (protocol == answer->protocol) {
- if (protocol != IPPROTO_IP)
- break;
- } else {
- /* Check for the two wild cases. */
- if (IPPROTO_IP == protocol) {
- protocol = answer->protocol;
- break;
- }
- if (IPPROTO_IP == answer->protocol)
- break;
- }
- err = -EPROTONOSUPPORT;
- answer = NULL;
- }
由于同一type不同protocol的套接字保存在inetsw中的同一链表中,因此需要遍历链表来查找;在上面的例子中,会将protocol重新赋值为answer->protocol,即IPPROTO_TCP,其值为6;
3) 使用匹配的协议族操作集初始化socket;
- sock->ops = answer->ops;
- answer_prot = answer->prot;// 供后面使用
结合例子,sock变量的ops指向inet_stream_ops结构体变量;
4) 分配sock结构体变量net/Socket.c:sys_socket()->sock_create()->__sock_create()->net/ipv4/Af_inet.c:inet_create()->net/core/Sock.c:sk_alloc():
- sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
其中,answer_prot指向tcp_prot结构体变量;
- struct sock *sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot) {
- struct sock *sk;
- sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
- if (sk) {
- sk->sk_family = family;
- sk->sk_prot = sk->sk_prot_creator = prot;
- sock_lock_init(sk);
- sock_net_set(sk, get_net(net));
- }
- return sk;
- }
其中,sk_prot_alloc分配sock结构体变量;由于在inet_init中为不同的套接字分配了高速缓冲区,因此该sock结构体变量会在该缓冲区中分配空间;分配完成后,对其做一些初始化工作:
i) 初始化sk变量的sk_prot和sk_prot_creator;
ii) 初始化sk变量的等待队列;
iii) 设置net空间结构,并增加引用计数;
6、建立socket结构与sock结构的关系:
1) socket, sock, inet_sock, tcp_sock的关系
创建完sk变量后,回到inet_create函数中:
- inet = inet_sk(sk);
- static inline struct inet_sock *inet_sk(const struct sock *sk)
- {
- return (struct inet_sock *)sk;
- }
这里是根据sk变量得到inet_sock变量的地址;细心的同学可能会问到:inet_sock是什么?之前分配的是sock变量,与inet_sock有什么关系啊?
a. struct socket:这个是基本的BSD socket,应用程序通过系统调用开始创建的socket都是该结构体,它是基于虚拟文件系统创建出来的;
类型主要有三种,即流式、数据报、原始套接字协议;
其状态比较粗粒度,如下:
- typedef enum {
- SS_FREE = 0, /* not allocated */
- SS_UNCONNECTED, /* unconnected to any socket */
- SS_CONNECTING, /* in process of connecting */
- SS_CONNECTED, /* connected to socket */
- SS_DISCONNECTING /* in process of disconnecting */
- } socket_state;
b. struct sock:它是网络层的socket;对应有TCP、UDP、RAW三种;
其状态相比socket结构更精细:
- enum {
- TCP_ESTABLISHED = 1,
- TCP_SYN_SENT,
- TCP_SYN_RECV,
- TCP_FIN_WAIT1,
- TCP_FIN_WAIT2,
- TCP_TIME_WAIT,
- TCP_CLOSE,
- TCP_CLOSE_WAIT,
- TCP_LAST_ACK,
- TCP_LISTEN,
- TCP_CLOSING, /* Now a valid state */
- TCP_MAX_STATES /* Leave at the end! */
- };
c. struct inet_sock:它是INET域的socket表示,是对struct sock的一个扩展,提供INET域的一些属性,如TTL,组播列表,IP地址,端口等;
d. struct raw_socket:它是RAW协议的一个socket表示,是对struct inet_sock的扩展,它要处理与ICMP相关的内容;
e. sturct udp_sock:它是UDP协议的socket表示,是对struct inet_sock的扩展;
f. struct inet_connection_sock:它是所有面向连接的socket表示,是对struct inet_sock的扩展;
g. struct tcp_sock:它是TCP协议的socket表示,是对struct inet_connection_sock的扩展,主要增加滑动窗口,拥塞控制一些TCP专用属性;
h. struct inet_timewait_sock:它是网络层用于超时控制的socket表示;
i. struct tcp_timewait_sock:它是TCP协议用于超时控制的socket表示;
上面简单介绍了一下内核中不同的socket相关的结构体的作用;回到inet_create函数中:
- inet = inet_sk(sk);
这里为什么能直接将sock结构体变量强制转化为inet_sock结构体变量呢?只有一种可能,那就是在分配sock结构体变量时,真正分配的是inet_sock或是其他结构体;
我们回到分配sock结构体的那块代码(参考前面的5.4小节:net/core/Sock.c):
- static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family) {
- struct sock *sk;
- struct kmem_cache *slab;
- slab = prot->slab;
- if (slab != NULL)
- sk = kmem_cache_alloc(slab, priority);
- else
- sk = kmalloc(prot->obj_size, priority);
- return sk;
- }
上面的代码在分配sock结构体时,有两种途径,一是从tcp专用高速缓存中分配;二是从内存直接分配;前者在初始化高速缓存时,指定了结构体大小为prot->obj_size;后者也有指定大小为prot->obj_size,
根据这点,我们看下tcp_prot变量中的obj_size(net/ipv4/Tcp_ipv4.c):
- .obj_size = sizeof(struct tcp_sock),
也就是说,分配的真实结构体是tcp_sock;由于tcp_sock、inet_connection_sock、inet_sock、sock之间均为0处偏移量,因此可以直接将tcp_sock直接强制转化为inet_sock;这几个结构体间的关系如下:
2) 建立socket, sock的关系
创建完sock变量之后,便是初始化sock结构体,并建立sock与socket之间的引用关系;调用链如下:
net/Socket.c:sys_socket()->sock_create()->__sock_create()->net/ipv4/Af_inet.c:inet_create()->net/core/Sock.c:sock_init_data():
该函数主要工作是:
a. 初始化sock结构的缓冲区、队列等;
b. 初始化sock结构的状态为TCP_CLOSE;
c. 建立socket与sock结构的相互引用关系;
7、使用tcp协议初始化sock:
inet_create()函数最后,通过相应的协议来初始化sock结构:
- if (sk->sk_prot->init) {
- err = sk->sk_prot->init(sk);
- if (err)
- sk_common_release(sk);
- }
例子中,这里调用的是tcp_prot的init钩子函数net/ipv4/Tcp_ipv4.c:tcp_v4_init_sock(),它主要是对tcp_sock和inet_connection_sock进行一些初始化;
8、socket与文件系统关联:
回到net/Socket.c:sys_socket()函数:
- asmlinkage long sys_socket(int family, int type, int protocol)
- {
- int retval;
- struct socket *sock;
- retval = sock_create(family, type, protocol, &sock);
- if (retval < 0)
- goto out;
- retval = sock_map_fd(sock);
- if (retval < 0)
- goto out_release;
- out:
- /* It may be already another descriptor 8) Not kernel problem. */
- return retval;
- out_release:
- sock_release(sock);
- return retval;
- }
创建好与socket相关的结构后,需要与文件系统关联,详见sock_map_fd()函数:
1) 申请文件描述符,并分配file结构和目录项结构;
2) 关联socket相关的文件操作函数表和目录项操作函数表;
3) 将file->private_date指向socket;
socket与文件系统关联后,以后便可以通过文件系统read/write对socket进行操作了;
小结:
1、socket库函数通过内核创建socket,并初始化其状态为TCP_CLOSE;
2、创建完后,与文件系统关联,其文件一般位于/proc/$pid/fd/目录下;
3、应用程序可以通过文件对socket进行操作;
Linux内核网络协议栈4-创建socket(2)相关推荐
- Linux内核网络协议栈7-socket端口管理
一.前情回顾 上一节<socket 地址绑定 >中提到,应用程序传递过来的端口在内核中需要检查端口是否可用: if (sk->sk_prot->get_port(sk, snu ...
- Linux内核网络协议栈5-socket地址绑定
一.socket绑定入口 1.示例代码 struct sockaddr_in server_address; server_address.sin_family = AF_INET; server_a ...
- Linux内核网络协议栈6-socket地址绑定(2)
三.根据不同的协议来完成绑定 上面代码中的第3步是根据应用程序在创建socket时传递到内核的协议域及socket类型来决定调用采用哪个方法,具体可以参考 创建socket 一文,这里不再赘 ...
- Linux内核网络协议栈2-socket从库函数到内核
一.socket()库函数到系统调用,再到内核 1.Linux运行的C库是glibc: 2.socket()调用如下: 1) socket()->__socket():glibc-2.3.6/s ...
- Linux内核网络协议栈1- socket文件系统注册
一.注册时机 1.在内核初始化时完成: 2.内核初始化过程(init/main.c): kernel_init()->do_basic_setup()->do_initcalls()-&g ...
- Linux内核网络协议栈:udp数据包发送(源码解读)
<监视和调整Linux网络协议栈:接收数据> <监控和调整Linux网络协议栈的图解指南:接收数据> <Linux网络 - 数据包的接收过程> <Linux网 ...
- Linux内核网络协议栈流程及架构
文章目录 Linux内核网络报文处理流程 Linux内核网络协议栈架构 Linux内核网络报文处理流程 linux网络协议栈是由若干个层组成的,网络数据的处理流程主要是指在协议栈的各个层之间的传递. ...
- 深入浅出Linux内核网络协议栈|结构sk_buff|Iptables|Netfilter丨内核源码丨驱动开发丨内核开发丨C/C++Linux服务器开发
深入浅出Linux内核网络协议栈 视频讲解如下,点击观看: 深入浅出Linux内核网络协议栈|结构sk C/C++Linux服务器开发高级架构师知识点精彩内容包括:C/C++,Linux,Nginx, ...
- Linux 内核网络协议栈运行原理
封装:当应用程序用 TCP 协议传送数据时,数据首先进入内核网络协议栈中,然后逐一通过 TCP/IP 协议族的每层直到被当作一串比特流送入网络.对于每一层而言,对收到的数据都会封装相应的协议首部信息( ...
- linux内核网络协议栈--linux网络设备理解(十三)
网络层次 linux网络设备驱动与字符设备和块设备有很大的不同. 字符设备和块设备对应/dev下的一个设备文件.而网络设备不存在这样的设备文件.网络设备使用套接字socket访问,虽然也使用read, ...
最新文章
- 简直让人欲罢不能!820个ML Python库,star超260万,持续周更中...
- php和python哪个用了开发web好-web开发选择Python还是PHP好呢?
- 【原创中】儿子,听爸爸跟你说
- python中int input_python中的input是什么
- Python数据分析-初识numpy、pandas、scipy、matplotlib和Scikit-Learn等数据处理库
- 万元大奖,FlyAI算法新赛事,心理卡牌目标检测
- cmmi评估如果可以再来
- matlab封闭传递包求解,梯度下降和封闭形式的解决方案 - MATLAB中不同的假设线...
- C# 多个图片叠加,图片透明.
- concat oracle 多个字符串_12个常用的JavaScript字符串方法
- 学习opencv3 pdf_PDF的虚拟打印机,免费又好用
- 【论文阅读】深度学习与多种机器学习方法在不同的药物发现数据集进行对比
- 小米路由器 内核 linux,小米路由器配置ssh登入方法教程
- vsftpd匿名登陆连接报错:500 OOPS: vsftpd: refusing to run with writable root inside chroot()(未解决)
- 【MySQL】听柠檬班公开课后,学习笔记及作业(二)
- win7系统设置电脑自动开机的操作方法
- elasticsearch通用工具类
- 【Promise学习】Promise的理解
- 菜鸟级别批处理命令IF~ELSE语法问题
- 国家英语四级考试词组
热门文章
- Java 算法 数字分类
- 鸿蒙硬件HI3861开发环境搭建
- sklearn K折(KFold)交叉验证案例,展开细节代码与cross_validate简写
- 创建目录_聊聊Word创建目录那些事儿
- java找不到数据库的表_GreenDao:no such table 找不到表的终极解决方案!
- 正则表达式 6. 存在(或)
- [BAT] 执行xcopy命令后出现Invalid num of parameters错误的解决办法
- nginx 在ubuntu 上的启动,停止,重启
- 《C++ Primer》关于自增自减操作符的描述错误
- 需要gmail的朋友请留下你们的email,还有86个