socket 注册 创建sock和关联协议,之后recv 和send都用的这个sock。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 sock = pal_sock (NSM_ZG, AF_INET, SOCK_RAW, IPPROTO_VRRP); | V inet_create case SOCK_RAW: if (!capable(CAP_NET_RAW)) goto free_and_badperm; if (!protocol) goto free_and_noproto; ... prot = &raw_prot; sk->reuse = 1 ; sk->ip_pmtudisc = IP_PMTUDISC_DONT; sk->num = protocol; ... sock->ops = &inet_dgram_ops; if (protocol == IPPROTO_RAW) sk->ip_hdrincl = 1 ;
说明: socket的创建初始化了以下模块
例:收包inet_recvmsg 发包inet_sendmsg
收包流程 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 inet_recvmsg(af_inet.c) | Vraw_recvmsg (raw.c) | Vskb_recv_datagram (sk->receive_queue) 从queue 取 ===========sk_recv_queue========================= skb_queue_tail 存入queue ^ | sock_queue_rcv_skb(sk->receive_queue) ^ | raw_rcv_skb ^ | raw_rcv ^ | ip_local_deliver ^ | skb->dst->input(skb) ^ | ip_route_input -> ip_route_input_slow ^ | ip_rcv <= ip_init_ict{dev_add_pack(&ip_packet_type)} ^ | net_bh ===========msg(net_mq)========================= mark_bh ^ | netif_rx ^ | hal_packet_rx
发包流程 传输层到网络层发送图
邻居层到物理层
UDP之数据报发送过程 https://blog.csdn.net/wangquan1992/article/details/109164638
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 inet_sockraw_ops | V raw_prot/udp_prot { 道: ->ip_build_xmit->ip_output_ict linux: raw_sendmsg ->ip_append_data ip_push_pending_frames | V ip_send_skb -> ip_local_out -> dst_output -> {skb_dst(skb)->output(net, sk, skb);} } tcp_prot { tcp_sendmsg->tcp_sendmsg_locked -> tcp_push_one -> tcp_write_xmit -> tcp_transmit_skb err = icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl); | V ip_queue_xmit } ==============ip skb->sk_write_queue 道和新版linux有点区别========================= ip_queue_xmit | V skb->dst->output = Ip_output_ict | V ip_finish_output | V dst->neighbout->output/hh->hh_output = dev_queue_xmit | V dev->hard_start_xmit | V ethVlanTx | V serialDevHardXmit | V hal_packet_tx linux ip_queue_xmit | V ip_local_out | V dst_output | V ip_output | V ip_finish_output | V dst->neighbout->output/hh->hh_output | V dev_queue_xmit -> dev_hard_start_xmit | V __dev_xmit_skb | V 看下面:软中断发送过程 dev_hard_start_xmit | V xmit_one | V netdev_start_xmit | V ops->ndo_start_xmit (dm9000)
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 1. ip_route_output_slow 设置路由缓存 后面用到的dst都是sk->dst_cache复制的 sk->dst_cache = &rt->u.dst tcp_v4_connect -> ip_route_connect -> ip_route_output -> ip_route_output_slow { rth->u.dst.output = ip_output_ict; rth->u.dst.input = ip_local_deliver; rt_intern_hash(vrfIndex, hash, rth, rp); } tcp_make_synack { skb->dst = dst_clone(dst); skb_dst_set(skb, dst); }2. neighbout设置 dst->neighbout->output = dev_queue_xmit arp_constructor(struct neighbour *neigh) { neigh->ops = &arp_direct_ops; neigh->ops = &arp_hh_ops; neigh->ops = &arp_generic_ops; }
「干货」25 张图一万字,拆解 Linux 网络包发送过程(下)
https://baijiahao.baidu.com/s?id=1707608648230579710&wfr=spider&for=pc
软中断发送过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void __qdisc_run(struct Qdisc *q) { int quota = weight_p; while (qdisc_restart(q)) { if (--quota <= 0 || need_resched()) { __netif_schedule(q); break ; } } }
while 循环不断地从队列中取出 skb 并进行发送。注意,这个时候其实都占用的是用户进程的系统态时间(sy)。只有当 quota 用尽或者其它进程需要 CPU 的时候才触发软中断进行发送。
该函数会进入到 __netif_reschedule,由它来实际发出 NET_TX_SOFTIRQ 类型软中断。是通过软中断
open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action);
软中断 软中断的实现难机制很多,比如bottom half(下半部)、task queue,不过这两者已经被废弃,下面三种实现机制是现在kernel还支持的:
软中断(softirq) :内核2.3引入,是最基本、最优先的软中断处理形式,为了避免名字冲突,本文中将这种子类型的软中断叫softirq。
tasklet :其底层使用softirq机制实现,提供了一种用户方便使用的软中方式,为软中断提供了很好的扩展性。
work queue :前两种软中断执行时是禁止抢占的(softirq的ksoftirq除外),对于用户进程不友好。如果在softirq执行时间过长,会继续推后到work queue中执行,work queue执行处于进程上下文,其可被抢占,也可以被调度,如果软中断需要执行睡眠、阻塞,直接选择work queue。
Linux的中断处理机制 : https://zhuanlan.zhihu.com/p/80371745
Linux内核浅析-软中断 :https://zhuanlan.zhihu.com/p/81742153
linux 中断子系统-softirq 的实现原理: https://zhuanlan.zhihu.com/p/363225717
s3c2440 linux中断过程:https://blog.csdn.net/oqqYuJi12345678/article/details/99726927
软中断的触发时机
1、irq_exit :在硬中断退出时,会检查local_softirq_pending和preemt_count,如果都符合条件,则执行软中断。
1 2 if (!in_interrupt() && local_softirq_pending()) invoke_softirq();
2、local_bh_enable :使用此函数开启软中断时,会检查local_softirq_pending,如果都符合条件,则执行软中断。调用链为local_bh_enable ()->__local_bh_enable()->do_softirq()。
3、raise_softirq :主动唤起一个软中断,会首先设置__softirq_pending对应的软中断位为挂起,然后检查in_interrupt,如果不在中断中,则唤起ksoftirq线程执行软中断(ksoftirq是softirq的一种执行机制,在软中的运行流程中会提到)。
软中断线程是在系统初始化时创建的,cpu几个核心就创建几个线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 smpboot_register_percpu_thread smpboot_thread_fn { if (!ht->thread_should_run (td-> cpu)) { }else { ht ->thread_fn (td-> cpu); } }