已阅读5页,还剩284页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
已完成-基于LINUX内核中的TCP/IP的核心过程分析在我的博客中的如何从应用程序进入linux内核日志中我详细分析了Unix的socket的创建、发送、接收、关闭的过程,而你看到下边这些文章是基于IPV4的追踪分析的过程。从围绕着服务器端的socket的建立-监听-接收连接-客户端发送连接请求-与服务器对接-数据接收-数据发送整个过程为主线分析。本文是核心过程分析所以重点对内核的代码进行剖析,在另一篇中TCP/IP协议内核源码分析中将完善理论及协议方面的内容,希望大家多提宝贵的意见,使本文成为一部经典的学习材料。内核中的TCP的追踪分析1-追踪TCP(IPV4)的socket的初始化内核中的TCP的追踪分析2-追踪TCP(IPV4)的socket的创建内核中的TCP的追踪分析3-TCP(IPV4)的socket的地址绑定内核中的TCP的追踪分析4-TCP(IPV4)的socket的地址绑定续内核中的TCP的追踪分析5-再谈TCP(IPV4)的socket的地址绑定内核中的TCP的追踪分析6-TCP(IPV4)的socket的监听内核中的TCP的追踪分析7-TCP(IPV4)的socket接收连接内核中的TCP的追踪分析8-TCP(IPV4)的socket连接内核中的TCP的追踪分析9-TCP(IPV4)的socket连接-续1内核中的TCP的追踪分析10-TCP(IPV4)的socket连接-续2内核中的TCP的追踪分析11-TCP(IPV4)的socket连接-续3内核中的TCP的追踪分析12-TCP(IPV4)的socket连接-续4内核中的TCP的追踪分析13-TCP(IPV4)的socket连接-续5内核中的TCP的追踪分析14-TCP(IPV4)的客户端与服务器端socket连接过程-1内核中的TCP的追踪分析15-TCP(IPV4)的客户端与服务器端socket连接过程-2内核中的TCP的追踪分析16-TCP(IPV4)的客户端与服务器端socket连接过程-3内核中的TCP的追踪分析17-TCP(IPV4)的客户端与服务器端socket连接过程-4内核中的TCP的追踪分析18-TCP(IPV4)的客户端与服务器端socket连接过程-5内核中的TCP的追踪分析19-TCP(IPV4)的服务器端数据的接收内核中的TCP的追踪分析20-TCP(IPV4)的服务器端数据的接收-续内核中的TCP的追踪分析21-TCP(IPV4)的客户端数据的发送内核中的TCP的追踪分析22-TCP(IPV4)的客户端数据的发送-续内核中的TCP的追踪分析1-追踪TCP(IPV4)的socket的初始化在我的博客中的 如何从实践引领进入linux内核类别日志中我详细分析了Unix的socket的创建、发送、接收、关闭的过程,这节开始进入探讨IPV4的TCP的socket的创建,我们在linux/unix的socket从实践到内核分析部分/u2/64681/showart.php?id=1287300中看到了关于socket的系统调用的总入口函数sys_socketcall()然后根据case SYS_SOCKET:err = sys_socket(a0, a1, a2);进入sys_socket()函数 asmlinkage long sys_socket(int family, int type, int protocol)。retval = sock_create(family, type, protocol, &sock);。再进入sock_create()最后进入_sock_create(),然后在那里执行err = pf-create(net, sock, protocol);这个过程我们在socket的实践到内核开始都追踪过了,不再细说了,我们这里直接从ipv4的socket的创建开始说起,我们回忆一下在有关的重要记忆点首先是net_families这个管理所有的网络协议的数组,我们说过在_sock_create()会在支持动态安装模块的前提下首先调用if (net_familiesfamily = NULL)request_module(net-pf-%d, family);也就是检查相应的协议有没有安装,我们在实践练习中曾经在创建socket用过这句 server_sockfd = socket(AF_INET, SOCK_STREAM, 0);由些传递下来的family参数则是AF_INET,这个值是2,也就是说我们要在net_families2处安装这里的tcp协议,那么在我们这里要谈到的网络协议是何时安装到数组中的呢?我是无名小卒,本文是原创如果转载请注明出处,我们在以前没提到过这里简要介绍一下,首先是内核在初始时会执行到init/main.c,而执行到内核的初始化函数kernel_init()在其内部调用了do_basic_setup()函数,再调用do_initcalls()函数,这里会看到有一个 for (call = _initcall_start; call family)err = -EEXIST;else net_familiesops-family = ops;err = 0;。这里会把 static struct net_proto_family inet_family_ops = .family = PF_INET,.create = inet_create,.owner= THIS_MODULE,;注册到net_families数组中,而PF_INET就是上面我们说的AF_INET #define PF_INETAF_INET所以这里我们的练习中会进经过pf-create(net, sock, protocol)到inet_create()中,下一篇待续 内核中的TCP的追踪分析2-追踪TCP(IPV4)的socket的创建今天真是不幸,写好的文章竟然在编辑器里被错误冲刷掉了,本来已经完成了本章节的内容,就要在最后关头出现了一个IE错误,现在是重写的,请朋友们见谅。我是无名小卒,请转载的朋友注明出处,谢谢。昨天我们看到了inet_create()函数我们今天继续。static int inet_create(struct net *net, struct socket *sock, int protocol)struct sock *sk;struct list_head *p;struct inet_protosw *answer;struct inet_sock *inet;struct proto *answer_prot;unsigned char answer_flags;char answer_no_check;int try_loading_module = 0;int err;if (sock-type != SOCK_RAW & sock-type != SOCK_DGRAM & !inet_ehash_secret)build_ehash_secret();sock-state = SS_UNCONNECTED;/* Look for the requested type/protocol pair. */answer = NULL;这个函数首先是检查是不是原始的socket和udp的socket,并且判断是否已经有了加密字符如果没有就会调用build_ehash_secret来分配一个void build_ehash_secret(void)u32 rnd;do get_random_bytes(&rnd, sizeof(rnd); while (rnd = 0);spin_lock_bh(&inetsw_lock);if (!inet_ehash_secret)inet_ehash_secret = rnd;spin_unlock_bh(&inetsw_lock);get_random_bytes是取得一个随机数即“熵”,取得后赋值给加密字符使用。然后上面的函数中将socket设置为未连接状态。我们继续往下看 lookup_protocol:err = -ESOCKTNOSUPPORT;rcu_read_lock();list_for_each_rcu(p, &inetswsock-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;if (unlikely(answer = NULL) if (try_loading_module 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);goto lookup_protocol; elsegoto out_rcu_unlock;err = -EPERM;if (answer-capability 0 & !capable(answer-capability)goto out_rcu_unlock;err = -EAFNOSUPPORT;if (!inet_netns_ok(net, protocol)goto out_rcu_unlock;sock-ops = answer-ops;answer_prot = answer-prot;answer_no_check = answer-no_check;answer_flags = answer-flags;rcu_read_unlock();BUG_TRAP(answer_prot-slab != NULL);err = -ENOBUFS;sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);if (sk = NULL)goto out;err = 0;sk-sk_no_check = answer_no_check;if (INET_PROTOSW_REUSE & answer_flags)sk-sk_reuse = 1;inet = inet_sk(sk);inet-is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;if (SOCK_RAW = sock-type) inet-num = protocol;if (IPPROTO_RAW = protocol)inet-hdrincl = 1;if (ipv4_config.no_pmtu_disc)inet-pmtudisc = IP_PMTUDISC_DONT;elseinet-pmtudisc = IP_PMTUDISC_WANT;inet-id = 0;sock_init_data(sock, sk);sk-sk_destruct = inet_sock_destruct;sk-sk_family = PF_INET;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_index= 0;inet-mc_list= NULL;sk_refcnt_debug_inc(sk);if (inet-num) /* It assumes that any protocol which allows * the user to assign a number at socket * creation time automatically * shares. */inet-sport = htons(inet-num);/* Add to protocol hash chains. */sk-sk_prot-hash(sk);if (sk-sk_prot-init) err = sk-sk_prot-init(sk);if (err)sk_common_release(sk);out:return err;out_rcu_unlock:rcu_read_unlock();goto out;这段代码看似复杂其实分析起来并不算难,首先上面 list_for_each_rcu(p, &inetswsock-type)是一个宏,我们看一下#define list_for_each_rcu(pos, head) for (pos = rcu_dereference(head)-next); prefetch(pos-next), pos != (head); pos = rcu_dereference(pos-next)这段宏我贴些资料供大家理解,下面这些内容出自/bookfiles/12/1001212.shtml RCU(Read-Copy Update)通过延迟写操作来提高同步性能,具体请参见第3章。这里只分析具有RCU的链表。RCU常用来保护读操作占多数的链表与数组。具有RCU的链表的操作函数与普通链表操作函数的区别是在函数名后加上了_rcu,如list_for_each_rcu函数。函数list_for_each_rcu的功能是遍历一个rcu保护的链表。其中,参数pos表示用来做链表位置计数的&struct list_head结构,参数head表示链表头。只要遍历被rcu_read_lock()保护,使用诸如list_add_rcu()的函数对链表同时访问是安全的。函数List_for_each_rcu列出如下:#define list_for_each_rcu(pos, head) for (pos = (head)-next, prefetch(pos-next); pos != (head); pos = rcu_dereference(pos-next), prefetch(pos-next)函数rcu_dereference在RCU读临界部分中取出一个RCU保护的指针。在需要内存屏障的体系中进行内存屏障(目前只有Alpha体系需要),函数列出如下:#define rcu_dereference(p) ( typeof(p) _p1 = p; smp_read_barrier_depends(); (_p1); )在include/asm-i386/system.h中:#define smp_read_barrier_depends()read_barrier_depends()很明显上面的宏就是循环检查inetsw数组找到符合我们socket类型的链头,那么这个数组是什么时候初始化的呢?我们再象上一节那样看一下static int _init inet_init(void)。for (q = inetsw_array; q protocol;struct list_head *last_perm;spin_lock_bh(&inetsw_lock);if (p-type = SOCK_MAX)goto out_illegal;/* If we are trying to override a permanent protocol, bail. */answer = NULL;last_perm = &inetswp-type;list_for_each(lh, &inetswp-type) answer = list_entry(lh, struct inet_protosw, list);/* Check only the non-wild match. */if (INET_PROTOSW_PERMANENT & answer-flags) if (protocol = answer-protocol)break;last_perm = lh;answer = NULL;if (answer)goto out_permanent;/* Add the new entry after the last permanent entry if any, so that * the new entry does not override a permanent entry when matched with * a wild-card protocol. But it is allowed to override any existing * non-permanent entry. This means that when we remove this entry, the * system automatically returns to the old behavior. */list_add_rcu(&p-list, last_perm);out:spin_unlock_bh(&inetsw_lock);synchronize_net();return;out_permanent:printk(KERN_ERR Attempt to override permanent protocol %d.n, protocol);goto out;out_illegal:printk(KERN_ERR Ignoring attempt to register invalid socket type %d.n, p-type);goto out;很明显在上面的循环中找到适合的链头位置,将我们的数组中的元素一一注册登记到数组中。我们这里要看一下inet_protosw结构struct inet_protosw struct list_head list;/* These two fields form the lookup key. */unsigned short type; /* This is the 2nd argument to socket(2). */unsigned short protocol; /* This is the L4 protocol number. */struct proto *prot;const struct proto_ops *ops;int capability; /* Which (if any) capability do * we need to use this socket * interface?*/char no_check; /* checksum on rcv/xmit/none? */unsigned char flags; /* See INET_PROTOSW_* below. */;这个结构是专门用于采用IP协议的socket使用。其内部的变量我们暂时不做分析,也不转译了,我们还是坚持“用时学习”的观念。回到inet_create()函数中,我们已经在inetsw数组中找到了我们的协议类型的链头就会取得其宿主inet_protosw 结构,然后answer = list_entry(p, struct inet_protosw, list);接着函数中对其兼容性进行了检测,最关键的地方是sock-ops = answer-ops;answer_prot = answer-prot;这二句首先是为socket的协议操作函数进行了挂钩,我们就要看上面的.type = SOCK_STREAM,.protocol = IPPROTO_TCP,.prot = &tcp_prot,.ops = &inet_stream_ops,.capability = -1,.no_check = 0,.flags = INET_PROTOSW_PERMANENT | INET_PROTOSW_ICSK,结合这个元素的设置我们明白了,上面socket的协议操作函数被设置成了inet_stream_ops(),而answer_prot设置成了tcp_prot结构。这是个struct proto结构,我们不看了,其内容很多,但是这个结构的作用得强调一下,它是专门用于socket的传输层使用的结构,而用于网络传输层的结构由另一个结构体来表示struct inet_proto。接着函数中分配了一个sock结构。我是无名小卒,尽管3月份才写博客其实研究内核很多年了,写这些博客是为了与朋友们共享知识发扬copyleft精神,所以请转载的朋友注明出处。分配函数我们曾经在unix的socket创建过程中谈到过,我们不细细研究这个函数了,不过要注意其内部的关键地方sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);我们看到他传递了一个answer_prot即我们说的用于socket传输层的钩子函数给sk_alloc。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;/* * See comment in struct sock definition to understand * why we need sk_prot_creator -acme */sk-sk_prot = sk-sk_prot_creator = prot;sock_lock_init(sk);sock_net_set(sk, get_net(net);return sk;注意上面sk-sk_prot = sk-sk_prot_creator = prot;将传输层的钩子函数挂入到了sock中了。再回到inet_create函数中,代码是一些对sock的初始化,注意sk是sock结构,而sock是socket结构。使用inet = inet_sk(sk);使sk赋值给struct inet_sock *inet结构变量,我们在unix的socket中曾经说了关于unix 的socket是unix_sock,而这里用于INET的socket结构inet_sock。然后函数通过sock_init_data(sock, sk);对sk进一步的初始化操作,并让sock和sk挂起钩来,这个函数我们已经在那些unix 的socket创建文章中分析过了,请不明白的朋友们看那里的文章学习。下面函数中最关键的地方if (sk-sk_prot-init) err = sk-sk_prot-init(sk);这个就是调用了我们上面提到的传输层结构中的钩子函数init.我们看到上面是tcp_prot结构变量。所以进入其init函数中struct proto tcp_prot = .name= TCP,.owner= THIS_MODULE,.close= tcp_close,.connect= tcp_v4_connect,.disconnect= tcp_disconnect,.accept= inet_csk_accept,.ioctl= tcp_ioctl,.init= tcp_v4_init_sock,.destroy= tcp_v4_destroy_sock,.shutdown= tcp_shutdown,.setsockopt= tcp_setsockopt,.getsockopt= tcp_getsockopt,.recvmsg= tcp_recvmsg,.backlog_rcv= tcp_v4_do_rcv,.hash= inet_hash,.unhash= inet_unhash,.get_port= inet_csk_get_port,.enter_memory_pressure= tcp_enter_memory_pressure,.sockets_allocated= &tcp_sockets_allocated,.orphan_count= &tcp_orphan_count,.memory_allocated= &tcp_memory_allocated,.memory_pressure= &tcp_memory_pressure,.sysctl_mem= sysctl_tcp_mem,.sysctl_wmem= sysctl_tcp_wmem,.sysctl_rmem= sysctl_tcp_rmem,.max_header= MAX_TCP_HEADER,.obj_size= sizeof(struct tcp_sock),.twsk_prot= &tcp_timewait_sock_ops,.rsk_prot= &tcp_request_sock_ops,.h.hashinfo= &tcp_hashinfo,#ifdef CONFIG_COMPAT.compat_setsockopt= compat_tcp_setsockopt,.compat_getsockopt= compat_tcp_getsockopt,#endif;我是无名小卒,转载请注明出处,不要担心结构有多么大,很多朋友在看代码时总是被结构所吓倒,要知道一个好的结构体也不是一天所成更不是一人所完成的,所以我们要针对场景来分析记忆。其实上面我们就只关心一个地方.init = tcp_v4_init_sock,,进入钩子函数static int tcp_v4_init_sock(struct sock *sk)struct inet_connection_sock *icsk = inet_csk(sk);struct tcp_sock *tp = tcp_sk(sk);skb_queue_head_init(&tp-out_of_order_queue);tcp_init_xmit_timers(sk);tcp_prequeue_init(tp);icsk-icsk_rto = TCP_TIMEOUT_INIT;tp-mdev = TCP_TIMEOUT_INIT;/* So many TCP implementations out there (incorrectly) count the * initial SYN frame in their delayed-ACK and congestion control * algorithms that we must have the following bandaid to talk * efficiently to them. -DaveM */tp-snd_cwnd = 2;/ zhenwei/* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. */tp-snd_ssthresh = 0x7fffffff;/* Infinity */tp-snd_cwnd_clamp = 0;tp-mss_cache = 536;tp-reordering = sysctl_tcp_reordering;icsk-icsk_ca_ops = &tcp_init_congestion_ops;sk-sk_state = TCP_CLOSE;sk-sk_write_space = sk_stream_write_space;sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);icsk-icsk_af_ops = &ipv4_specific;icsk-icsk_sync_mss = tcp_sync_mss;#ifdef CONFIG_TCP_MD5SIGtp-af_specific = &tcp_sock_ipv4_specific;#endifsk-sk_sndbuf = sysctl_tcp_wmem1;sk-sk_rcvbuf = sysctl_tcp_rmem1;atomic_inc(&tcp_sockets_allocated);return 0;首先函数出现了一个新的结构体struct tcp_sock,这个结构非常大,但是其作用随着我们的分析会越来越清晰,上面代码中对这个结构变量tp和sock结构变量sk进一步的初始化,对后这个地方的初始化工作我们要经常回顾,例如这里的sk-sk_write_space = sk_stream_write_space等钩子函数的挂入,以及缓冲区的相关设置。最后我们回到上一节提到的sys_socket()函数中执行retval = sock_map_fd(sock)来完成创建过程,sock_map_fd我们在unix的socket中详细分析了,他在文件系统中分配一个文件号,以及file文件指针和目录项dentry结构使其与socket挂上钩,最后返回分配的文件号,至此以后我们可以根据这个文件号对创建的socket进行其他操作了。相关内容看unix的socket创建/u2/64681/showart_1300200.html。内核中的TCP的追踪分析3-TCP(IPV4)的socket的地址绑定今天我们继续内核中的TCP的socket的学习,同样按照Unix那节中的socket地址绑定的路线我们来分析一下,我们看到在以前的练习中有这样的绑定代码bind(server_sockfd, (struct sockaddr *)&server_address, server_len);上面的练习请参考/u2/6
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 核桃直播营销方案(3篇)
- 福建新风营销方案(3篇)
- 2025新能源产业市场现状及投资评估和规划分析研究
- 2025新成就行业市场规模调研及未来趋势与投资潜力评估研究报告
- 2025新加坡高端医疗器械研发行业市场现状动态分析及投资评估规划研究报告
- 2025新加坡能源制造业市场深度调研及市场分析与发展趋势研究报告
- 2025新加坡生物医药行业市场深度分析及创新药物研发与临床试验报告
- 5G技术背景下用户行为特征分析-洞察及研究
- 第三单元第7课视力不良影响职业发展课件华中师大版体育与健康七年级全一册
- 媒体融合背景下受众情感共鸣机制研究-洞察及研究
- 2025年变电设备检修工(中级)技能鉴定理论考试题库(含答案)
- 2025年电磁学试题及答案解析
- 2025年商铺停车管理合同协议
- 仓库盘点数据差异分析报告
- 湖北环境保护与可持续发展策略探讨
- 国家开放大学《人文英语3》写作复习参考答案
- 建筑联合体合作协议法律条款详解
- 解读2025 ECIL建议:血液学恶性肿瘤患者或接受造血细胞移植患者的社区获得性呼吸道病毒感染(更新版)
- 工业园蒸汽管道调试及启动方案
- 安装工程质量通病与防止措施
- 2025年深圳低空经济产业政策与「政策+支持」分析报告
评论
0/150
提交评论