浅析ethx网卡发送函数sys_write简易流程

来源:互联网 发布:华为交换机端口应用acl 编辑:程序博客网 时间:2024/05/01 14:53

浅析ethx网卡发送函数sys_write简易流程


sys_write=>vfs_write=>do_sync_write=>
filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
{也就是对应socket_file_ops中的sock_aio_write}
sock_aio_write=>do_sock_write=>__sock_sendmsg=>
sock->ops->sendmsg(iocb, sock, msg, size);
{也就是对应inetsw_array[]中的inet_stream_ops下的tcp_sendmsg}
tcp_sendmsg=>经过skb拷贝之后,最终在退出之前调用tcp_push,将数据发送到网络=>
tcp_push=>__tcp_push_pending_frames=>tcp_write_xmit=>tcp_transmit_skb=>
icsk->icsk_af_ops->queue_xmit(skb, 0);
/*
在tcp_v4_init_sock中初始化的如下方法:
icsk->icsk_af_ops = &ipv4_specific;
icsk->icsk_ca_ops = &tcp_init_congestion_ops;
tp->reordering = sysctl_tcp_reordering;
sk->sk_write_space = sk_stream_write_space;
icsk->icsk_sync_mss = tcp_sync_mss;
tp->af_specific = &tcp_sock_ipv4_specific;
所以icsk_af_ops就是ipv4_specific,
struct inet_connection_sock_af_ops ipv4_specific = {
    .queue_xmit     = ip_queue_xmit,
    .send_check     = tcp_v4_send_check,
    .rebuild_header     = inet_sk_rebuild_header,
    .conn_request     = tcp_v4_conn_request,
    .syn_recv_sock     = tcp_v4_syn_recv_sock,
    .remember_stamp     = tcp_v4_remember_stamp,
    .net_header_len     = sizeof(struct iphdr),
    .setsockopt     = ip_setsockopt,
    .getsockopt     = ip_getsockopt,
    .addr2sockaddr     = inet_csk_addr2sockaddr,
    .sockaddr_len     = sizeof(struct sockaddr_in),
#ifdef CONFIG_COMPAT
    .compat_setsockopt = compat_ip_setsockopt,
    .compat_getsockopt = compat_ip_getsockopt,
#endif
};
*/

ip_queue_xmit=>
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
            dst_output);
也就是调用dst_output将package发送到网络中出去.
/* Output packet to network from transport. */
static inline int dst_output(struct sk_buff *skb)
{
    return skb->dst->output(skb);
}
__mkroute_output=>dst_alloc=>
rth = dst_alloc(&ipv4_dst_ops);然后dst->ops = ipv4_dst_ops;
rth->u.dst.output=ip_output;
ip_output=>
return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
             ip_finish_output,
             !(IPCB(skb)->flags & IPSKB_REROUTED));
ip_finish_output=>ip_finish_output2=>
/*
    if (dst->hh)
        return neigh_hh_output(dst->hh, skb);
    else if (dst->neighbour)
        return dst->neighbour->output(skb);
*/


/*
static struct neigh_ops arp_generic_ops = {
    .family =        AF_INET,
    .solicit =        arp_solicit,
    .error_report =        arp_error_report,
    .output =        neigh_resolve_output,
    .connected_output =    neigh_connected_output,
    .hh_output =        dev_queue_xmit,
    .queue_xmit =        dev_queue_xmit,
};

static struct neigh_ops arp_hh_ops = {
    .family =        AF_INET,
    .solicit =        arp_solicit,
    .error_report =        arp_error_report,
    .output =        neigh_resolve_output,
    .connected_output =    neigh_resolve_output,
    .hh_output =        dev_queue_xmit,
    .queue_xmit =        dev_queue_xmit,
};

static struct neigh_ops arp_direct_ops = {
    .family =        AF_INET,
    .output =        dev_queue_xmit,
    .connected_output =    dev_queue_xmit,
    .hh_output =        dev_queue_xmit,
    .queue_xmit =        dev_queue_xmit,
};

struct neigh_ops arp_broken_ops = {
    .family =        AF_INET,
    .solicit =        arp_solicit,
    .error_report =        arp_error_report,
    .output =        neigh_compat_output,
    .connected_output =    neigh_compat_output,
    .hh_output =        dev_queue_xmit,
    .queue_xmit =        dev_queue_xmit,
};
*/

因为ip通信都是以arp开始来获取通信对端的mac地址,所以arp应该是网络通信的第一步,
这样就调用到了dev_queue_xmit()想物理网卡发送网络数据包,
dev_queue_xmit=>dev_hard_start_xmit=>
return dev->hard_start_xmit(skb, dev);
就是网卡驱动的发送函数wlan_hard_start_xmit,比较粗糙,凑活着看吧【gliethttp.Leith】


看看sys_socket()对空间结构的申请步骤:
sk->sk_prot = tcp_prot;
他的.obj_size = sizeof(struct tcp_sock),所以
struct tcp_sock {
    /* inet_connection_sock has to be the first member of tcp_sock */
    struct inet_connection_sock    inet_conn;
    ...
}
struct inet_connection_sock {
    /* inet_sock has to be the first member! */
    struct inet_sock     icsk_inet;
    ...
}
struct inet_sock {
    /* sk and pinet6 has to be the first two members of inet_sock */
    struct sock        sk;
    ...
}
所以sk_alloc申请到的sk,指向的是tcp_sock下inet_connection_sock下inet_sock中的sk,
然后
__sock_create=>
pf->create(net, sock, protocol);
inet_create=>
sk->sk_prot->init(sk)也就是调用tcp_prot的init,也就是tcp_v4_init_sock【gliethttp.Leith】.