日期:2014-05-16 浏览次数:20953 次
之前一篇写的不完整,重新写一篇
OpenWRT数据发送过程 这里使用的是ath9k网卡驱动,硬件平台是TP-link TL-WR841N V7.1 路由器
1. packet_sendmsg()
Linux kernel发送数据的接口函数是packet_sendmsg,本质上对应了user space的sendmsg实现。上层通过调用sendmsg实现数据的发送。将待发送的数据放入kernel space中。
在内核文件夹linux-3.3.8的子目录:/net/packet中,找到文件af_packet.c,这个文件里定义了如下一个结构:
static const struct proto_ops packet_ops = { … .sendmsg = packet_sendmsg, .recvmsg = packet_recvmsg, … };这个结构采用了C99标准的初始化方式,基本用法是“.成员=变量值”(已经预先定义了一个proto_ops结构,里面包含sendmsg函数指针)。这个结构中把上层的sendmsg函数和下层的packet_sendmsg函数对应了起来。上层通过调用sendmsg就将数据传递给了packet_sendmsg函数。下面我们就从内核态的packet_sendmsg出发来研究一下数据的发送过程。
static int packet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); if (po->tx_ring.pg_vec) return tpacket_snd(po, msg); else return packet_snd(sock, msg, len); }
static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) { ... // 首先把数据从user space拷贝到kernel space err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov, vnet_hdr_len); ... /* * Now send it */ // 然后用dev_queue_xmit()来发送skb. err = dev_queue_xmit(skb); if (err > 0 && (err = net_xmit_errno(err)) != 0) goto out_unlock; ... }
int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct netdev_queue *txq; struct Qdisc *q; int rc = -ENOMEM; skb_reset_mac_header(skb); /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ rcu_read_lock_bh(); skb_update_prio(skb); txq = netdev_pick_tx(dev, skb); q = rcu_dereference_bh(txq->qdisc); #ifdef CONFIG_NET_CLS_ACT skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS); #endif trace_net_dev_queue(skb); if (q->enqueue) { rc = __dev_xmit_skb(skb, q, dev, txq); goto out; } ... }
static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, struct net_device *dev, struct netdev_queue *txq) { ... if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) { kfree_skb(skb); rc = NET_XMIT_DROP; } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) && qdisc_run_begin(q)) { /* * This is a work-conserving queue; there are no old skbs * waiting to be sent out; and the qdisc is not running - * xmit the skb directly. */ if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE)) skb_dst_force(skb); qdisc_bstats_update(q, skb); // 注意这里 if (sch_direct_xmit(skb, q, dev, txq, root_lock)) { if (unlikely(