struct socket {

socket_state state;shorttype;

unsignedlongflags;

struct socket_wq*wq;

structfile *file;

struct sock*sk;

const struct proto_ops*ops;

};

在socket中Proto_ops结构体定义如下:

structproto_ops {intfamily;struct module *owner;int (*release) (struct socket *sock);int (*bind) (struct socket *sock,struct sockaddr *myaddr,intsockaddr_len);int (*connect) (struct socket *sock,struct sockaddr *vaddr,int sockaddr_len, intflags);int (*socketpair)(struct socket *sock1,struct socket *sock2);int (*accept) (struct socket *sock,struct socket *newsock, int flags, boolkern);int (*getname) (struct socket *sock,struct sockaddr *addr,intpeer);

__poll_t (*poll) (struct file *file, struct socket *sock,struct poll_table_struct *wait);int (*ioctl) (struct socket *sock, unsigned intcmd,

unsignedlongarg);

#ifdef CONFIG_COMPATint (*compat_ioctl) (struct socket *sock, unsigned intcmd,

unsignedlongarg);#endif

int (*listen) (struct socket *sock, intlen);int (*shutdown) (struct socket *sock, intflags);int (*setsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, unsigned intoptlen);int (*getsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, int __user *optlen);

#ifdef CONFIG_COMPATint (*compat_setsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, unsigned intoptlen);int (*compat_getsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, int __user *optlen);#endif

int (*sendmsg) (struct socket *sock, struct msghdr *m,

size_t total_len);/*Notes for implementing recvmsg:

* ===============================

* msg->msg_namelen should get updated by the recvmsg handlers

* iff msg_name != NULL. It is by default 0 to prevent

* returning uninitialized memory to user space. The recvfrom

* handlers can assume that msg.msg_name is either NULL or has

* a minimum size of sizeof(struct sockaddr_storage).*/

int (*recvmsg) (struct socket *sock, struct msghdr *m,

size_t total_len,intflags);int (*mmap) (struct file *file, struct socket *sock,struct vm_area_struct *vma);

ssize_t (*sendpage) (struct socket *sock, struct page *page,int offset, size_t size, intflags);

ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,struct pipe_inode_info *pipe, size_t len, unsigned intflags);int (*set_peek_off)(struct sock *sk, intval);int (*peek_len)(struct socket *sock);/*The following functions are called internally by kernel with

* sock lock already held.*/

int (*read_sock)(struct sock *sk, read_descriptor_t *desc,

sk_read_actor_t recv_actor);int (*sendpage_locked)(struct sock *sk, struct page *page,int offset, size_t size, intflags);int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg,

size_t size);int (*set_rcvlowat)(struct sock *sk, intval);

};

在上次是实验中,已经追踪到bind函数之后,在进行安全检查之后,调用了ops结构体中的listen指针指向的方法

在经过单步调试之后,

ops结构体中间的各个函数指针指向了net/ipv4/af_inet文件,我们接下来去找找这个文件中的各个方法

对该文件中各个方法打上断点

按照断点运行,可得过程如下:

上图中断点运行步骤展示了服务器端的函数执行顺序如下:

Inet_create->inet_bind ->inet_listen ->accept

因此TCP初始化过程大部分都是在create函数中完成的:

static int inet_create(struct net *net, struct socket *sock, intprotocol,intkern)

{struct sock *sk;struct inet_protosw *answer;struct inet_sock *inet;struct proto *answer_prot;

unsignedcharanswer_flags;int try_loading_module = 0;interr;if (protocol < 0 || protocol >=IPPROTO_MAX)return -EINVAL;

sock->state =SS_UNCONNECTED;/*Look for the requested type/protocol pair.*/lookup_protocol:

err= -ESOCKTNOSUPPORT;

rcu_read_lock();

list_for_each_entry_rcu(answer,&inetsw[sock->type], list) {

err= 0;/*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;

}if(unlikely(err)) {if (try_loading_module < 2) {

rcu_read_unlock();/** Be more specific, e.g. net-pf-2-proto-132-type-1

* (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)*/

if (++try_loading_module == 1)

request_module("net-pf-%d-proto-%d-type-%d",

PF_INET, protocol, sock->type);/** Fall back to generic, e.g. net-pf-2-proto-132

* (net-pf-PF_INET-proto-IPPROTO_SCTP)*/

elserequest_module("net-pf-%d-proto-%d",

PF_INET, protocol);gotolookup_protocol;

}else

gotoout_rcu_unlock;

}

err= -EPERM;if (sock->type == SOCK_RAW && !kern &&

!ns_capable(net->user_ns, CAP_NET_RAW))gotoout_rcu_unlock;

sock->ops = answer->ops;

answer_prot= answer->prot;

answer_flags= answer->flags;

rcu_read_unlock();

WARN_ON(!answer_prot->slab);

err= -ENOBUFS;

sk=sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);if (!sk)goto out;

err= 0;if (INET_PROTOSW_REUSE &answer_flags)

sk->sk_reuse =SK_CAN_REUSE;

inet=inet_sk(sk);

inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;

inet->nodefrag = 0;if (SOCK_RAW == sock->type) {

inet->inet_num =protocol;if (IPPROTO_RAW ==protocol)

inet->hdrincl = 1;

}if (net->ipv4.sysctl_ip_no_pmtu_disc)

inet->pmtudisc =IP_PMTUDISC_DONT;elseinet->pmtudisc =IP_PMTUDISC_WANT;

inet->inet_id = 0;

sock_init_data(sock, sk);

sk->sk_destruct =inet_sock_destruct;

sk->sk_protocol =protocol;

sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;

inet->uc_ttl = -1;

inet->mc_loop = 1;

inet->mc_ttl = 1;

inet->mc_all = 1;

inet->mc_index = 0;

inet->mc_list =NULL;

inet->rcv_tos = 0;

sk_refcnt_debug_inc(sk);if (inet->inet_num) {/*It assumes that any protocol which allows

* the user to assign a number at socket

* creation time automatically

* shares.*/inet->inet_sport = htons(inet->inet_num);/*Add to protocol hash chains.*/err= sk->sk_prot->hash(sk);if(err) {

sk_common_release(sk);goto out;

}

}if (sk->sk_prot->init) {

err= sk->sk_prot->init(sk);if(err) {

sk_common_release(sk);goto out;

}

}if (!kern) {

err=BPF_CGROUP_RUN_PROG_INET_SOCK(sk);if(err) {

sk_common_release(sk);goto out;

}

}out:returnerr;

out_rcu_unlock:

rcu_read_unlock();goto out;

}

那么Linux的 TCP协议中又是通过什么来切换状态的呢?

在逐步跟踪之后,我们找到了如下状态切换函数

tcp_set_state(sk, TCP_SYN_SENT);

err=inet_hash_connect(tcp_death_row, sk);if(err)gotofailure;

sk_set_txhash(sk);

rt=ip_route_newports(fl4, rt, orig_sport, orig_dport,

inet->inet_sport, inet->inet_dport, sk);if(IS_ERR(rt)) {

err=PTR_ERR(rt);

rt=NULL;goto failure;

tcp_set_state(sk, TCP_SYN_SENT)把套接字的状态从CLOSE切换到SYN_SENT,并进一步调用了 tcp_connect(sk)来实际构造SYN并发送出去。然后调用inet_hash_connect(&tcp_death_row, sk)和

ip_route_newports(fl4, rt, orig_sport, orig_dport, inet->inet_sport, inet->inet_dport, sk),为套接字绑定一个端口,并记录在TCP的哈希表中。

总的来说,整个过程的函数调用关系是这样的:

tcp_v4_connect -> tcp_connect_init -> tcp_transmit_skb -> icsk->icsk_af_ops->send_check (tcp_v4_send_check)-> icsk->icsk_af_ops->queue_xmit (ip_queue_xmit)-> inet_csk_reset_xmit_timer

总体来说TCP的初始化过程大概就是如上文所示了,最后放一张TCP状态转换图

linux初始化TCP服务失败,深入Linux系统追踪TCP初始化相关推荐

  1. linux启动网络服务的命令,linux重启服务命令

    linux重启服务命令 重启:service 服务名 restart 或systemctl restart 服务名 service和systemctl 1.service命令 service命令其实是 ...

  2. linux操作系统的cron服务用于管理,Linux的系统管理

    Linux的系统管理 一.Linux的系统启动的过程 1.Linux操作系统的启动的过程一般包括以下几个阶段: a) 主机启动并进行硬盘自检后,读取硬盘mbr中的启动引导器并进行加载. b) 启动引导 ...

  3. linux停止network服务命令是,Linux系统服务启动和停止

    systemd 是在Linux上运行服务的新方式. systemd 有一个被替代的 sysvinit . systemd 为Linux带来更快的启动时间,现在是管理Linux服务的标准方式.虽然稳定, ...

  4. linux关闭telnet服务的命令,linux如何开启telnet服务

    linux系统你只到吗,他可以开启telnet服务,那linux如何开启telnet服务的呢?下面是学习啦小编收集整理的linux如何开启telnet服务,希望对大家有帮助~~ linux开启teln ...

  5. Linux查hudi服务的进程,Linux查看非root运行的进程

    Linux查看非root运行的进程 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ ps -U root -u root -N PID TTY TIME CMD ...

  6. linux mysql端口启动失败怎么办,Linux下apache mysql等服务修改默认端口后无法正常启动解决办法...

    Linux下apache mysql等服务修改默认端口后无法正常启动解决办法 linux下 apache 等服务修改默认端口后无法正常启动解决办法 服务器上装了两个webserver,一个是nginx ...

  7. linux有关网络服务的接口,linux系统有关网络服务接口定义是哪个?

    浮云间 (1)网络接口的命名 这里并不存在一定的命名规范,但网络接口名字的定义一般都是要有意义的.例如: eth0: ethernet的简写,一般用于以太网接口. wifi0:wifi是无线局域网,因 ...

  8. Linux添加http服务失败,Apache Web服务器配置http2各种失败

    我在 Debian Stretch 最新稳定版 9.6 的系统下通过 Apt 源安装了 Apache 2.4.25 ,想配置 http2 协议支持,首先 a2enmod 了 http2 模块,确保已开 ...

  9. linux d path失败返回,linux – elasticsearch systemd服务失败

    我刚刚在linux 14.10上切换到systemd,现在我的弹性搜索服务没有正常启动 elasticsearch.service loaded failed failed systemctl状态为我 ...

最新文章

  1. 项目需求 | PC VR头戴显示器实时三维坐标(项目经费20万)
  2. java与fabric区块链--fabric-java-jdk部署搭建--(1)
  3. MapReduce示例——WordCount(统计单词)
  4. [Array]Pascal's Triangle II
  5. javaScript变量、作用域链
  6. redis设置开机自启动
  7. MAUI安卓子系统调试方法(附安装教程)
  8. webgl坐标转换_OpenGL/WebGL顶点坐标变换过程简介
  9. jQuery 利用 $.getJson() 实现跨域
  10. 如何优化网站页面提高网页的加载速度
  11. Ubuntu 完全卸载 Apache2
  12. input数字开头不能为0_Python新手上车5:数字和注释
  13. [Node] 基础知识
  14. 树为什么能长这么高?
  15. Event的三个阶段:CAPTURING_PHASE,AT_TARGET,BUBBLING_PHASE
  16. cad中填充的剖面线不能被修剪_为什么CAD中填充无法修剪?
  17. Switch新机发布引全球吐槽
  18. GDR(Gradual Decoder Refresh)帧
  19. Server 2008改成个人习惯的配置
  20. python --获取内网IP地址

热门文章

  1. linux命令的-和--参数问题
  2. 绝对不能错过!计算机视觉Polygon Mesh Processing读书笔记——3
  3. 推荐系统多兴趣召回最新进展
  4. 人类社会发明的第一台计算机是什么,第二周第一课概述计算机
  5. 2021年用独立站铁赚的四种模式
  6. 课外题:需要排序的子数组
  7. TensorFlow2.0:梯度计算
  8. 花书+吴恩达深度学习(十七)序列模型之长短期记忆 LSTM 和门控循环单元 GRU
  9. Iptables(2) - iptables命令的基本用法
  10. unity如何检测内存泄漏_如何排查Java内存泄漏?看懂这一篇就够用了