接上篇“创建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;

C代码  
  1. sock->state = SS_UNCONNECTED;

2) 根据socket的type找到对应的套接字类型:

C代码  
  1. list_for_each_rcu(p, &inetsw[sock->type]) {
  2. answer = list_entry(p, struct inet_protosw, list);
  3. /* Check the non-wild match. */
  4. if (protocol == answer->protocol) {
  5. if (protocol != IPPROTO_IP)
  6. break;
  7. } else {
  8. /* Check for the two wild cases. */
  9. if (IPPROTO_IP == protocol) {
  10. protocol = answer->protocol;
  11. break;
  12. }
  13. if (IPPROTO_IP == answer->protocol)
  14. break;
  15. }
  16. err = -EPROTONOSUPPORT;
  17. answer = NULL;
  18. }

由于同一type不同protocol的套接字保存在inetsw中的同一链表中,因此需要遍历链表来查找;在上面的例子中,会将protocol重新赋值为answer->protocol,即IPPROTO_TCP,其值为6;

3) 使用匹配的协议族操作集初始化socket;

C代码  
  1. sock->ops = answer->ops;
  2. 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():

C代码  
  1. sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);

其中,answer_prot指向tcp_prot结构体变量;

C代码  
  1. struct sock *sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot) {
  2. struct sock *sk;
  3. sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
  4. if (sk) {
  5. sk->sk_family = family;
  6. sk->sk_prot = sk->sk_prot_creator = prot;
  7. sock_lock_init(sk);
  8. sock_net_set(sk, get_net(net));
  9. }
  10. return sk;
  11. }

其中,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函数中:

C代码  
  1. inet = inet_sk(sk);
  2. static inline struct inet_sock *inet_sk(const struct sock *sk)
  3. {
  4. return (struct inet_sock *)sk;
  5. }

这里是根据sk变量得到inet_sock变量的地址;细心的同学可能会问到:inet_sock是什么?之前分配的是sock变量,与inet_sock有什么关系啊? 
a. struct socket:这个是基本的BSD socket,应用程序通过系统调用开始创建的socket都是该结构体,它是基于虚拟文件系统创建出来的; 
类型主要有三种,即流式、数据报、原始套接字协议; 
其状态比较粗粒度,如下:

C代码  
  1. typedef enum {
  2. SS_FREE = 0,            /* not allocated        */
  3. SS_UNCONNECTED,         /* unconnected to any socket    */
  4. SS_CONNECTING,          /* in process of connecting */
  5. SS_CONNECTED,           /* connected to socket      */
  6. SS_DISCONNECTING        /* in process of disconnecting  */
  7. } socket_state;

b. struct sock:它是网络层的socket;对应有TCP、UDP、RAW三种; 
其状态相比socket结构更精细:

C代码  
  1. enum {
  2. TCP_ESTABLISHED = 1,
  3. TCP_SYN_SENT,
  4. TCP_SYN_RECV,
  5. TCP_FIN_WAIT1,
  6. TCP_FIN_WAIT2,
  7. TCP_TIME_WAIT,
  8. TCP_CLOSE,
  9. TCP_CLOSE_WAIT,
  10. TCP_LAST_ACK,
  11. TCP_LISTEN,
  12. TCP_CLOSING,    /* Now a valid state */
  13. TCP_MAX_STATES  /* Leave at the end! */
  14. };

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函数中:

C代码  
  1. inet = inet_sk(sk);

这里为什么能直接将sock结构体变量强制转化为inet_sock结构体变量呢?只有一种可能,那就是在分配sock结构体变量时,真正分配的是inet_sock或是其他结构体;

我们回到分配sock结构体的那块代码(参考前面的5.4小节:net/core/Sock.c):

C代码  
  1. static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family) {
  2. struct sock *sk;
  3. struct kmem_cache *slab;
  4. slab = prot->slab;
  5. if (slab != NULL)
  6. sk = kmem_cache_alloc(slab, priority);
  7. else
  8. sk = kmalloc(prot->obj_size, priority);
  9. return sk;
  10. }

上面的代码在分配sock结构体时,有两种途径,一是从tcp专用高速缓存中分配;二是从内存直接分配;前者在初始化高速缓存时,指定了结构体大小为prot->obj_size;后者也有指定大小为prot->obj_size, 
根据这点,我们看下tcp_prot变量中的obj_size(net/ipv4/Tcp_ipv4.c):

C代码  
  1. .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结构:

C代码  
  1. if (sk->sk_prot->init) {
  2. err = sk->sk_prot->init(sk);
  3. if (err)
  4. sk_common_release(sk);
  5. }

例子中,这里调用的是tcp_prot的init钩子函数net/ipv4/Tcp_ipv4.c:tcp_v4_init_sock(),它主要是对tcp_sock和inet_connection_sock进行一些初始化;

8、socket与文件系统关联: 
回到net/Socket.c:sys_socket()函数:

C代码  
  1. asmlinkage long sys_socket(int family, int type, int protocol)
  2. {
  3. int retval;
  4. struct socket *sock;
  5. retval = sock_create(family, type, protocol, &sock);
  6. if (retval < 0)
  7. goto out;
  8. retval = sock_map_fd(sock);
  9. if (retval < 0)
  10. goto out_release;
  11. out:
  12. /* It may be already another descriptor 8) Not kernel problem. */
  13. return retval;
  14. out_release:
  15. sock_release(sock);
  16. return retval;
  17. }

创建好与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)相关推荐

  1. Linux内核网络协议栈7-socket端口管理

    一.前情回顾 上一节<socket 地址绑定 >中提到,应用程序传递过来的端口在内核中需要检查端口是否可用: if (sk->sk_prot->get_port(sk, snu ...

  2. Linux内核网络协议栈5-socket地址绑定

    一.socket绑定入口 1.示例代码 struct sockaddr_in server_address; server_address.sin_family = AF_INET; server_a ...

  3. Linux内核网络协议栈6-socket地址绑定(2)

    三.根据不同的协议来完成绑定   上面代码中的第3步是根据应用程序在创建socket时传递到内核的协议域及socket类型来决定调用采用哪个方法,具体可以参考   创建socket  一文,这里不再赘 ...

  4. Linux内核网络协议栈2-socket从库函数到内核

    一.socket()库函数到系统调用,再到内核 1.Linux运行的C库是glibc: 2.socket()调用如下: 1) socket()->__socket():glibc-2.3.6/s ...

  5. Linux内核网络协议栈1- socket文件系统注册

    一.注册时机 1.在内核初始化时完成: 2.内核初始化过程(init/main.c): kernel_init()->do_basic_setup()->do_initcalls()-&g ...

  6. Linux内核网络协议栈:udp数据包发送(源码解读)

    <监视和调整Linux网络协议栈:接收数据> <监控和调整Linux网络协议栈的图解指南:接收数据> <Linux网络 - 数据包的接收过程> <Linux网 ...

  7. Linux内核网络协议栈流程及架构

    文章目录 Linux内核网络报文处理流程 Linux内核网络协议栈架构 Linux内核网络报文处理流程 linux网络协议栈是由若干个层组成的,网络数据的处理流程主要是指在协议栈的各个层之间的传递. ...

  8. 深入浅出Linux内核网络协议栈|结构sk_buff|Iptables|Netfilter丨内核源码丨驱动开发丨内核开发丨C/C++Linux服务器开发

    深入浅出Linux内核网络协议栈 视频讲解如下,点击观看: 深入浅出Linux内核网络协议栈|结构sk C/C++Linux服务器开发高级架构师知识点精彩内容包括:C/C++,Linux,Nginx, ...

  9. Linux 内核网络协议栈运行原理

    封装:当应用程序用 TCP 协议传送数据时,数据首先进入内核网络协议栈中,然后逐一通过 TCP/IP 协议族的每层直到被当作一串比特流送入网络.对于每一层而言,对收到的数据都会封装相应的协议首部信息( ...

  10. linux内核网络协议栈--linux网络设备理解(十三)

    网络层次 linux网络设备驱动与字符设备和块设备有很大的不同. 字符设备和块设备对应/dev下的一个设备文件.而网络设备不存在这样的设备文件.网络设备使用套接字socket访问,虽然也使用read, ...

最新文章

  1. 简直让人欲罢不能!820个ML Python库,star超260万,持续周更中...
  2. php和python哪个用了开发web好-web开发选择Python还是PHP好呢?
  3. 【原创中】儿子,听爸爸跟你说
  4. python中int input_python中的input是什么
  5. Python数据分析-初识numpy、pandas、scipy、matplotlib和Scikit-Learn等数据处理库
  6. 万元大奖,FlyAI算法新赛事,心理卡牌目标检测
  7. cmmi评估如果可以再来
  8. matlab封闭传递包求解,梯度下降和封闭形式的解决方案 - MATLAB中不同的假设线...
  9. C# 多个图片叠加,图片透明.
  10. concat oracle 多个字符串_12个常用的JavaScript字符串方法
  11. 学习opencv3 pdf_PDF的虚拟打印机,免费又好用
  12. 【论文阅读】深度学习与多种机器学习方法在不同的药物发现数据集进行对比
  13. 小米路由器 内核 linux,小米路由器配置ssh登入方法教程
  14. vsftpd匿名登陆连接报错:500 OOPS: vsftpd: refusing to run with writable root inside chroot()(未解决)
  15. 【MySQL】听柠檬班公开课后,学习笔记及作业(二)
  16. win7系统设置电脑自动开机的操作方法
  17. elasticsearch通用工具类
  18. 【Promise学习】Promise的理解
  19. 菜鸟级别批处理命令IF~ELSE语法问题
  20. 国家英语四级考试词组

热门文章

  1. Java 算法 数字分类
  2. 鸿蒙硬件HI3861开发环境搭建
  3. sklearn K折(KFold)交叉验证案例,展开细节代码与cross_validate简写
  4. 创建目录_聊聊Word创建目录那些事儿
  5. java找不到数据库的表_GreenDao:no such table 找不到表的终极解决方案!
  6. 正则表达式 6. 存在(或)
  7. [BAT] 执行xcopy命令后出现Invalid num of parameters错误的解决办法
  8. nginx 在ubuntu 上的启动,停止,重启
  9. 《C++ Primer》关于自增自减操作符的描述错误
  10. 需要gmail的朋友请留下你们的email,还有86个