之前一篇写的不完整,重新写一篇
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出发来研究一下数据的发送过程。
下面来看看packet_sendmsg()的实现(位于linux-3.3.8/net/packet文件夹下的af_packet.c文件中)
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);
}
2. packet_snd()
调用packet_snd()(位于linux-3.3.8/net/packet文件夹下的af_packet.c文件中)。
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;
...
}
3. dev_queue_xmit()
调用dev_queue_xmit()(位于linux-3.3.8/net/core文件夹下的dev.c文件中)。
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;
}
...
}
4. __dev_xmit_skb()
调用__dev_xmit_skb()(位于linux-3.3.8/net/core文件夹下的dev.c文件中)。
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)) {
/*