1. 程式人生 > >Linux無線網路架構

Linux無線網路架構

簡介

 Android中無線網路的軟體涉及linux核心、supplicant、

framework、wifi service,程式碼從c、c++、java都有,這一篇主
要介紹linux核心中的無線網路。要了解linux的無線網路,首先要
瞭解linux的網路架構,接著介紹無線網路的架構,然後分析網路數
據包的收、發流程。

1 Linux的網路架構

首先看一下linux的網路架構
這裡寫圖片描述

 系統呼叫介面
系統呼叫介面可以從兩個角度進行描述。使用者發起網路呼叫時,通過系統呼叫介面進入核心的過程應該是多路的。最後呼叫 ./net/socket.c 中的 sys_socketcall 結束該過程,然後進一步將呼叫分路傳送到指定目標。系統呼叫介面的另一種描述是使用普通檔案操作作為網路 I/O。例如,典型的讀寫操作可以在網路 socket 上執行(socket 使用一個檔案描述符表示,與一個普通檔案一樣)。因此,儘管有很多操作是網路專用的(使用 socket 呼叫建立一個 socket,使用 connect 呼叫連線一個收信方,等等),但是也有一些標準的檔案操作可以應用於網路物件,就像操作普通檔案一樣。最後,系統呼叫介面提供了在使用者空間應用程式和核心之間轉移控制的方法。

 協議無關介面
socket 層是一個協議無關介面,它提供了一組通用函式來支援各種不同協議。socket 層不但可以支援典型的 TCP 和 UDP 協議,而且還可以支援 IP、裸乙太網和其他傳輸協議,例如 SCTP(Stream Control Transmission Protocol)。
通過網路棧進行的通訊都需要對 socket 進行操作。Linux 中的 socket 結構是 struct sock,這個結構是在 linux/include/net/sock.h 中定義的。這個巨大的結構中包含了特定 socket 所需要的所有狀態資訊,其中包括 socket 所使用的特定協議和在 socket 上可以執行的一些操作。
網路子系統可以通過一個定義了自己功能的特殊結構來了解可用協議。每個協議都維護了一個名為 proto 的結構(可以在 linux/include/net/sock.h 中找到)。這個結構定義了可以在從 socket 層到傳輸層中執行特定的 socket 操作(例如,如何建立一個 socket,如何使用 socket 建立一個連線,如何關閉一個 socket 等等)。

 網路協議
網路協議這一節對一些可用的特定網路協議作出了定義(例如 TCP、UDP 等)。它們都是在 linux/net/ipv4/af_inet.c 檔案中一個名為 inet_init 的函式中進行初始化的(因為 TCP 和 UDP 都是 inet 簇協議的一部分)。 inet_init 函式使用 proto_register 函式來註冊每個內嵌協議。這個函式是在 linux/net/core/sock.c 中定義的,除了可以將這個協議新增到活動協議列表中之外,如果需要,該函式還可以選擇分配一到多個 slab 快取。
通過 linux/net/ipv4/ 目錄中 udp.c 和 raw.c 檔案中的 proto 介面,您可以瞭解各個協議是如何標識自己的。這些協議介面每個都按照型別和協議對映到 inetsw_array,該陣列將內嵌協議與操作對映到一起。inetsw_array 結構及其關係如圖所示。最初,會呼叫 inet_init 中的 inet_register_protosw 將這個陣列中的每個協議都初始化為 inetsw。函式 inet_init 也會對各個 inet 模組進行初始化,例如 ARP、ICMP 和 IP 模組,以及 TCP 和 UDP 模組。
這裡寫圖片描述

 裝置無關介面
協議層下面是另外一個無關介面層,它將協議與具有很多各種不同功能的硬體裝置連線在一起。這一層提供了一組通用函式供底層網路裝置驅動程式使用,讓它們可以對高層協議棧進行操作。
首先,裝置驅動程式可能會通過呼叫 register_netdevice 或 unregister_netdevice 在核心中進行註冊或登出。呼叫者首先填寫 net_device 結構,然後傳遞這個結構進行註冊。核心呼叫它的 init 函式(如果定義了這種函式),然後執行一組健全性檢查,並建立一個 sysfs 條目,然後將新裝置新增到裝置列表中(核心中的活動裝置連結串列)。在 linux/include/linux/netdevice.h 中可以找到這個 net_device 結構。這些函式都是在 linux/net/core/dev.c 中實現的。
要從協議層向裝置中傳送 sk_buff,就需要使用 dev_queue_xmit 函式。這個函式可以對 sk_buff 進行排隊,從而由底層裝置驅動程式進行最終傳輸(使用 sk_buff 中引用的 net_device 或 sk_buff->dev 所定義的網路裝置)。dev 結構中包含了一個名為 hard_start_xmit 的方法,其中儲存有發起 sk_buff 傳輸所使用的驅動程式函式。
報文的接收通常是使用 netif_rx 執行的。當底層裝置驅動程式接收一個報文(包含在所分配的 sk_buff 中)時,就會通過呼叫 netif_rx 將 sk_buff 上傳至網路層。然後,這個函式通過 netif_rx_schedule 將 sk_buff 在上層協議佇列中進行排隊,供以後進行處理。可以在 linux/net/core/dev.c 中找到 dev_queue_xmit 和 netif_rx 函式。

 裝置驅動程式
網路棧底部是負責管理物理網路裝置的裝置驅動程式。例如,包串列埠使用的 SLIP 驅動程式以及乙太網裝置使用的乙太網驅動程式都是這一層的裝置。
在進行初始化時,裝置驅動程式會分配一個 net_device 結構,然後使用必須的程式對其進行初始化。這些程式中有一個是 dev->hard_start_xmit,它定義了上層應該如何對 sk_buff 排隊進行傳輸。這個程式的引數為 sk_buff。這個函式的操作取決於底層硬體,但是通常 sk_buff 所描述的報文都會被移動到硬體環或佇列中。

 網路介面的註冊
一個網絡卡,要能夠被核心使用,必須通過register_netdev介面註冊進核心。register_netdev的引數為net_device結構體,其中net_device結構中的netdev_ops成員涉及資料傳送,網路操作的介面。下面給出了register_netdev和netdev_ops的結構體及boardcom的netdev_ops的定義。對於網路資料包接收,是直接呼叫核心介面netif_rx把資料包傳給核心,在下面的章節在介紹。

kernel\include\linux\netdevice.h
struct net_device {

    /*
     * This is the first field of the "visible" part of this structure
     * (i.e. as seen by users in the "Space.c" file).  It is the name
     * of the interface.
     */
……..  /* 省略中間程式碼  */

    /* Management operations */
    const struct net_device_ops *netdev_ops;
    const struct ethtool_ops *ethtool_ops;
kernel\include\linux\netdevice.h
struct net_device_ops {
    int         (*ndo_init)(struct net_device *dev);
    void            (*ndo_uninit)(struct net_device *dev);
    int         (*ndo_open)(struct net_device *dev);
    int         (*ndo_stop)(struct net_device *dev);
    netdev_tx_t     (*ndo_start_xmit) (struct sk_buff *skb,
                           struct net_device *dev);
    u16         (*ndo_select_queue)(struct net_device *dev,
                            struct sk_buff *skb);
    void            (*ndo_change_rx_flags)(struct net_device *dev,
                               int flags);
    void            (*ndo_set_rx_mode)(struct net_device *dev);
    int         (*ndo_set_mac_address)(struct net_device *dev,
                               void *addr);
    int         (*ndo_validate_addr)(struct net_device *dev);
    int         (*ndo_do_ioctl)(struct net_device *dev,
                            struct ifreq *ifr, int cmd);
    int         (*ndo_set_config)(struct net_device *dev,
                              struct ifmap *map);
    int         (*ndo_change_mtu)(struct net_device *dev,
                          int new_mtu);
    int         (*ndo_neigh_setup)(struct net_device *dev,
                           struct neigh_parms *);
    void            (*ndo_tx_timeout) (struct net_device *dev);
kernel\drivers\net\wireless\actions\bcmdhd\dhd_linux.c
static struct net_device_ops dhd_ops_pri = {
    .ndo_open = dhd_open,
    .ndo_stop = dhd_stop,
    .ndo_get_stats = dhd_get_stats,
    .ndo_do_ioctl = dhd_ioctl_entry,
    .ndo_start_xmit = dhd_start_xmit,
    .ndo_set_mac_address = dhd_set_mac_address,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
    .ndo_set_rx_mode = dhd_set_multicast_list,
#else
    .ndo_set_multicast_list = dhd_set_multicast_list,
#endif
};
kernel\drivers\net\wireless\actions\bcmdhd\dhd_linux.c
int
dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock)
{
    dhd_info_t *dhd = (dhd_info_t *)dhdp->info;

……..  /* 省略中間程式碼  */

    if (ifidx == 0) {
        /*
         * device functions for the primary interface only
         */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
        net->open = dhd_open;
        net->stop = dhd_stop;
#else
        net->netdev_ops = &dhd_ops_pri;
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
        if (!ETHER_ISNULLADDR(dhd->pub.mac.octet))
            memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
    } 

……..  /* 省略中間程式碼  */

    if (need_rtnl_lock)
        err = register_netdev(net);
    else
        err = register_netdevice(net);

2 Linux的無線架構

對於Linux的無線架構,可以分成兩部分來學習,第一部分為資料包的收發過程,這部分與1節所說的一樣。第二部分為無線網路的控制部分,包括無線網路的掃描、連線、斷開及無線網路的各種設定及查詢。下圖為一個較完整的無線架構。
這裡寫圖片描述
上面的圖看起來涉及比較多的內容,而且各種架構都存在,總的來說可以分成2大部分,無線網路控制部分及無線網路資料傳輸部分,資料傳輸部分與2.1節介紹的一樣。控制部分中,應用層Wext方式的通道已經不使用,現在大部分都是使用libnl方式。對於驅動架構的方式,核心中3種使用方式都有使用。當從我們現在使用的驅動程式碼看,都是使用方式3的驅動架構。在這隻介紹方式3的驅動架構,在方式3的驅動架構的程式碼中,mac80211已經看不到痕跡了,在這主要介紹一下nl80211及cfg80211。
 nl80211
無線網路在應用層使用libnl(Netlink Library)對命令進行了一層封裝,應用層對無線網路的操作全部是通過libnl提供的介面,而libnl與核心的互動最終會走到nl80211(nl80211.c)。nl80211的核心結構如下,包含了所有的無線網路操作命令。

\kernel\include\net\genetlink.h
/**
 * struct genl_ops - generic netlink operations
 * @cmd: command identifier
 * @internal_flags: flags used by the family
 * @flags: flags
 * @policy: attribute validation policy
 * @doit: standard command callback
 * @dumpit: callback for dumpers
 * @done: completion callback for dumps
 * @ops_list: operations list
 */
struct genl_ops {
    u8          cmd;
    u8          internal_flags;
    unsigned int        flags;
    const struct nla_policy *policy;
    int            (*doit)(struct sk_buff *skb,
                       struct genl_info *info);
    int            (*dumpit)(struct sk_buff *skb,
                     struct netlink_callback *cb);
    int            (*done)(struct netlink_callback *cb);
    struct list_head    ops_list;
};
kernel\net\wireless\nl80211.c
static struct genl_ops nl80211_ops[] = {
    {
        .cmd = NL80211_CMD_GET_WIPHY,
        .doit = nl80211_get_wiphy,
        .dumpit = nl80211_dump_wiphy,
        .policy = nl80211_policy,
        /* can be retrieved by unprivileged users */
        .internal_flags = NL80211_FLAG_NEED_WIPHY,
    },
    {
        .cmd = NL80211_CMD_SET_WIPHY,
        .doit = nl80211_set_wiphy,
        .policy = nl80211_policy,
        .flags = GENL_ADMIN_PERM,
        .internal_flags = NL80211_FLAG_NEED_RTNL,
    },
     ……        /* 省略中間部分程式碼 */
     {
        .cmd = NL80211_CMD_VENDOR,
        .doit = nl80211_vendor_cmd,
        .policy = nl80211_policy,
        .flags = GENL_ADMIN_PERM,
        .internal_flags = NL80211_FLAG_NEED_WIPHY |
                  NL80211_FLAG_NEED_RTNL,
    },
};

 cfg80211_ops
cfg80211是Linux 802.11配置API, 用於對無線裝置進行配置管理,用於連線nl80211和硬體的操作,每一個不同的wifi驅動都有自己的一份cfg80211_ops程式碼,在初始化時通過wiphy_register註冊進核心。下面為boardcom的cfg80211_ops程式碼:

kernel\drivers\net\wireless\actions\bcmdhd\ wl_cfg80211.c
static struct cfg80211_ops wl_cfg80211_ops = {
    .add_virtual_intf = wl_cfg80211_add_virtual_iface,
    .del_virtual_intf = wl_cfg80211_del_virtual_iface,
    .change_virtual_intf = wl_cfg80211_change_virtual_iface,
#if defined(WL_CFG80211_P2P_DEV_IF)
    .start_p2p_device = wl_cfgp2p_start_p2p_device,
    .stop_p2p_device = wl_cfgp2p_stop_p2p_device,
#endif /* WL_CFG80211_P2P_DEV_IF */
    .scan = wl_cfg80211_scan,
    .set_wiphy_params = wl_cfg80211_set_wiphy_params,
    .join_ibss = wl_cfg80211_join_ibss,
    .leave_ibss = wl_cfg80211_leave_ibss,
    .get_station = wl_cfg80211_get_station,
    .set_tx_power = wl_cfg80211_set_tx_power,
    .get_tx_power = wl_cfg80211_get_tx_power,
    .add_key = wl_cfg80211_add_key,
    .del_key = wl_cfg80211_del_key,
    .get_key = wl_cfg80211_get_key,
    .set_default_key = wl_cfg80211_config_default_key,
    .set_default_mgmt_key = wl_cfg80211_config_default_mgmt_key,
    .set_power_mgmt = wl_cfg80211_set_power_mgmt,
    .connect = wl_cfg80211_connect,
    .disconnect = wl_cfg80211_disconnect,
    .suspend = wl_cfg80211_suspend,
    .resume = wl_cfg80211_resume,
    .set_pmksa = wl_cfg80211_set_pmksa,
    .del_pmksa = wl_cfg80211_del_pmksa,
    .flush_pmksa = wl_cfg80211_flush_pmksa,
    .remain_on_channel = wl_cfg80211_remain_on_channel,
    .cancel_remain_on_channel = wl_cfg80211_cancel_remain_on_channel,
    .mgmt_tx = wl_cfg80211_mgmt_tx,
    .mgmt_frame_register = wl_cfg80211_mgmt_frame_register,
    .change_bss = wl_cfg80211_change_bss,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) || defined(WL_COMPAT_WIRELESS)
    .set_channel = wl_cfg80211_set_channel,
#endif /* ((LINUX_VERSION < VERSION(3, 6, 0)) || WL_COMPAT_WIRELESS */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) && !defined(WL_COMPAT_WIRELESS)
    .set_beacon = wl_cfg80211_add_set_beacon,
    .add_beacon = wl_cfg80211_add_set_beacon,
#else
    .change_beacon = wl_cfg80211_change_beacon,
    .start_ap = wl_cfg80211_start_ap,
    .stop_ap = wl_cfg80211_stop_ap,
#endif /* LINUX_VERSION < KERNEL_VERSION(3,4,0) && !WL_COMPAT_WIRELESS */
#ifdef WL_SCHED_SCAN
    .sched_scan_start = wl_cfg80211_sched_scan_start,
    .sched_scan_stop = wl_cfg80211_sched_scan_stop,
#endif /* WL_SCHED_SCAN */
#if defined(WL_SUPPORT_BACKPORTED_KPATCHES) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
    .del_station = wl_cfg80211_del_station,
    .change_station = wl_cfg80211_change_station,
    .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait,
#endif /* WL_SUPPORT_BACKPORTED_KPATCHES || KERNEL_VERSION >= (3,2,0) */
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) || defined(WL_COMPAT_WIRELESS)
    .tdls_oper = wl_cfg80211_tdls_oper,
#endif /* LINUX_VERSION > VERSION(3, 2, 0) || WL_COMPAT_WIRELESS */
#ifdef WL_SUPPORT_ACS
    .dump_survey = wl_cfg80211_dump_survey,
#endif /* WL_SUPPORT_ACS */
#ifdef WL_CFG80211_ACL
    .set_mac_acl = wl_cfg80211_set_mac_acl,
#endif /* WL_CFG80211_ACL */
};

3 Linux核心網路資料傳送流程

網路協議繁多,但大體框架相同,下面以傳送一個ipv4 tcp資料包為例,說明網路資料包的傳送流程,下圖為Linux tcp資料包的傳送流程。
這裡寫圖片描述

各層主要函式以及位置功能說明:
1) SYSCALL_DEFINE4(send):kernel/net/socket.c;socket傳送資料包系統呼叫介面;
2) SYSCALL_DEFINE6(sendto):kernel/net/socket.c;send呼叫sendto(sendto也是系統呼叫),填充struct msghdr結構體;
3) sock_sendmsg: kernel/net/socket.c;
4) __sock_sendmsg: kernel/net/socket.c;
5) __sock_sendmsg_nosec:kernel/net/socket.c;選擇tcp、udp、raw data協議傳送資料包。

kernel/net/socket.c
static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock,
                       struct msghdr *msg, size_t size)
{
    struct sock_iocb *si = kiocb_to_siocb(iocb);

    si->sock = sock;
    si->scm = NULL;
    si->msg = msg;
    si->size = size;

    return sock->ops->sendmsg(iocb, sock, msg, size);
}

const struct proto_ops inet_stream_ops = {   /* tcp資料包處理 */
    .family        = PF_INET,
    .owner         = THIS_MODULE,
    .release       = inet_release,
    .bind          = inet_bind,
    .connect       = inet_stream_connect,
    .socketpair    = sock_no_socketpair,
    .accept        = inet_accept,
    .getname       = inet_getname,
    .poll          = tcp_poll,
    .ioctl         = inet_ioctl,
    .listen        = inet_listen,
    .shutdown      = inet_shutdown,
    .setsockopt    = sock_common_setsockopt,
    .getsockopt    = sock_common_getsockopt,
    .sendmsg       = inet_sendmsg,
    .recvmsg       = inet_recvmsg,

const struct proto_ops inet_dgram_ops = {    /* udp資料包處理 */
    .family        = PF_INET,
    .owner         = THIS_MODULE,
    .release       = inet_release,
    .bind          = inet_bind,
    .connect       = inet_dgram_connect,
    .socketpair    = sock_no_socketpair,
    .accept        = sock_no_accept,
    .getname       = inet_getname,
    .poll          = udp_poll,
    .ioctl         = inet_ioctl,
    .listen        = sock_no_listen,
    .shutdown      = inet_shutdown,
    .setsockopt    = sock_common_setsockopt,
    .getsockopt    = sock_common_getsockopt,
    .sendmsg       = inet_sendmsg,
    .recvmsg       = inet_recvmsg,

static const struct proto_ops inet_sockraw_ops = {   /* raw資料包處理 */
    .family        = PF_INET,
    .owner         = THIS_MODULE,
    .release       = inet_release,
    .bind          = inet_bind,
    .connect       = inet_dgram_connect,
    .socketpair    = sock_no_socketpair,
    .accept        = sock_no_accept,
    .getname       = inet_getname,
    .poll          = datagram_poll,
    .ioctl         = inet_ioctl,
    .listen        = sock_no_listen,
    .shutdown      = inet_shutdown,
    .setsockopt    = sock_common_setsockopt,
    .getsockopt    = sock_common_getsockopt,
    .sendmsg       = inet_sendmsg,
    .recvmsg       = inet_recvmsg,

6) inet_sendmsg:kernel\net\ipv4\ af_inet.c;選擇ipv4處理資料傳送

kernel\net\ipv4\ af_inet.c
int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
         size_t size)
{
    struct sock *sk = sock->sk;

    sock_rps_record_flow(sk);

    /* We may need to bind the socket. */
    if (!inet_sk(sk)->inet_num && !sk->sk_prot->no_autobind &&
        inet_autobind(sk))
        return -EAGAIN;

    return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}
kernel\net\ipv4\tcp_ipv4.c
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,
    .sendmsg        = tcp_sendmsg,

7) tcp_sendmsg:kernel\net\ipv4\tcp.c;申請sk_buff{}結構的空間,把msghdr{}結構中的資料填入sk_buff空間;
8) tcp_push_one:kernel\net\ipv4\ tcp_output.c;
9) tcp_write_xmit:kernel\net\ipv4\ tcp_output.c;
10) tcp_transmit_skb:kernel\net\ipv4\ tcp_output.c;進行tcp封包,傳給ip層;

kernel\net\ipv4\ tcp_output.c
static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
                gfp_t gfp_mask)
{
    const struct inet_connection_sock *icsk = inet_csk(sk);
    struct inet_sock *inet;

    ……        /* 省略中間部分程式碼 */

    err = icsk->icsk_af_ops->queue_xmit(skb, &inet->cork.fl);
kernel\net\ipv4\tcp_ipv4.c
const 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,
    .sk_rx_dst_set     = inet_sk_rx_dst_set,
    .conn_request      = tcp_v4_conn_request,
    .syn_recv_sock     = tcp_v4_syn_recv_sock,
    .net_header_len    = sizeof(struct iphdr),
    .setsockopt    = ip_setsockopt,
    .getsockopt    = ip_getsockopt,
    .addr2sockaddr     = inet_csk_addr2sockaddr,
    .sockaddr_len      = sizeof(struct sockaddr_in),
    .bind_conflict     = inet_csk_bind_conflict,
#ifdef CONFIG_COMPAT
    .compat_setsockopt = compat_ip_setsockopt,
    .compat_getsockopt = compat_ip_getsockopt,
#endif
};

11) ip_queue_xmit:kernel\net\ipv4\ ip_output.c;ip層封包;
12) ip_local_out:kernel\net\ipv4\ ip_output.c;
13) __ip_local_out:kernel\net\ipv4\ ip_output.c;呼叫netfilter進行處理;
14) dst_output:kernel\include\net\dst.h;
15) ip_output:kernel\net\ipv4\ ip_output.c;
16) ip_finish_output:kernel\net\ipv4\ ip_output.c;
17) ip_finish_output2:kernel\net\ipv4\ ip_output.c;
18) dst_neigh_output:kernel\include\net\ neighbour.h;
19) neigh_hh_output或neigh_resolve_output; kernel\include\net\ neighbour.h 或kernel\net\core\neighbour.c;
20) dev_queue_xm it:kernel\net\core\dev.c;
21) dev_hard_start_xmit:kernel\net\core\dev.c;
22) ndo_start_xmit:網絡卡驅動註冊的傳送介面。

4 Linux核心網路資料接收流程

Linux的網路資料包接收流程以接收一個ipv4 tcp資料包為例,接收流程如下圖所示:
這裡寫圖片描述

各層主要函式以及位置功能說明:
 從上往下呼叫介面如下:
1) SYSCALL_DEFINE6(recvfrom):kernel/net/socket.c;socket接收資料包系統呼叫介面;
2) sock_recvmsg:kernel/net/socket.c;
3) __sock_recvmsg:kernel/net/socket.c;
4) __sock_recvmsg_nosec::kernel/net/socket.c;呼叫函式指標sock->ops->recvmsg完成在INET Socket層的資料接收過程,其中sock->ops被初始化為inet_stream_ops。
5) inet_recvmsg:kernel\net\ipv4\ af_inet.c;呼叫函式指標sk->sk_prot->recvmsg,recvmsg被初始化為tcp_recvmsg;
6) tcp_recvmsg:kernel\net\ipv4\tcp.c;從網路協議棧接收資料的動作,自上而下的觸發動作一直到這個函式為止,出現了一次等待的過程。函式tcp_recvmsg可能會被動地等待在sk的接收資料佇列上,也就是說,系統中肯定有其他地方會去修改這個佇列使得tcp_recvmsg可以進行下去,入口引數sk是這個網路連線對應的sock{}指標,msg用於存放接收到的資料。當有資料時,直接返回獲取的資料,若沒有資料時,則在sk_receive_queue上等待,等待底層收到資料後喚醒。

 下層接收到資料包後通知上層介面如下:
1) netif_rx:kernel\net\core\dev.c; wifi驅動接收到資料包後呼叫介面把資料包傳給核心,同時該函式呼叫enqueue_to_backlog 把資料包放入input_pkt_queue佇列,同時ip_rcv函式從input_pkt_queue取出資料包,再推送給上層;
2) ip_rcv:kernel\net\ipv4\ip_input.c;
3) ip_rcv_finish:kernel\net\ipv4\ip_input.c;ip_rcv和ip_rcv_finish從乙太網接收資料,放到skb裡,作ip層的一些資料及選項檢查,呼叫ip_route_input() 做路由處理,判斷是進行ip轉發還是將資料傳遞到高一層的協議。呼叫skb->dst->input函式指標,這個指標的實現可能有多種情況,如果路由得到的結果說明這個資料包應該轉發到其他主機,這裡的input便是ip_forward;如果資料包是給本機的,那麼input指標初始化為ip_local_deliver;
4) dst_input:kernel\include\net\dst.h;dst_input 函式呼叫skb_dst(skb)->input(skb),input函式指標被初始化為ip_local_deliver;
5) ip_local_deliver:kernel\net\ipv4\ ip_input.c;
6) ip_local_deliver_finish:kernel\net\ipv4\ ip_input.c; ip_local_deliver、ip_local_deliver_finish入口引數skb存放需要傳送到上層協議的資料,從ip頭中獲取是否已經分拆的資訊,如果已經分拆,則呼叫函式ip_defrag將資料包重組。然後通過呼叫ip_prot->handler指標呼叫 tcp_v4_rcv(tcp)。ip_prot是inet_protocol結構指標,是用來ip層登記協議的,比如由udp,tcp,icmp等協議;
7) tcp_v4_rcv:kernel\net\ipv4\tcp_ipv4.c;
8) tcp_v4_do_rcv:kernel\net\ipv4\tcp_ipv4.c;
9) tcp_rcv_established:kernel\net\ipv4\tcp_ipv4.c;
10) tcp_queue_rcv:kernel\net\ipv4\tcp_ipv4.c;tcp_v4_rcv被ip_local_deliver函式呼叫,是從IP層協議向INET Socket層提交的“資料到”請求,入口引數skb存放接收到的資料,len是接收的資料的長度,這個函式首先移動skb->data指標,讓它指向tcp頭,然後更新tcp層的一些資料統計,然後進行tcp的一些值的校驗,再從INET Socket層中已經建立的sock{}結構變數中查詢正在等待當前到達資料的哪一項,可能這個sock{}結構已經建立,或者還處於監聽埠、等待資料連線的狀態。返回的sock結構指標存放在sk中。然後根據其他程序對sk的操作情況,將skb傳送到合適的位置,呼叫如下:TCP包接收器(tcp_v4_rcv)將TCP包投遞到目的套接字進行接收處理。當套接字正被使用者鎖定,TCP包將暫時排入該套接字的後備佇列(sk_add_backlog),這時如果某一使用者執行緒企圖鎖定該套接字(lock_sock),該執行緒被排入套接字的後備處理等待佇列(sk->lock.wq),當用戶釋放上鎖的套接字時 (release_sock在tcp_recvmsg中呼叫),後備佇列中的TCP包被立即注入TCP包處理器(tcp_v4_do_rcv)進行處理,然後喚醒等待佇列中最先的一個使用者來獲得其鎖定權。如果套接字未被上鎖,當用戶正在讀取該套接字時,TCP包將被排入套接字的預備佇列(tcp_prequeue),將其傳遞到該使用者執行緒上下文中進行處理,如果新增到sk->prequeue不成功,便可以新增到sk_receive_queue佇列中。

相關推薦

Linux無線網路架構

簡介 Android中無線網路的軟體涉及linux核心、supplicant、 framework、wifi service,程式碼從c、c++、java都有,這一篇主 要介紹linux核心中的無線網路。要了解linux的無線網路,首先要 瞭

VMware Workstation環境下的Linux無線網路設定

【VMware網路連線】 VMware提供了三種將虛擬網絡卡和物理網絡卡捆綁在一起的方式,即橋接(Bridge)模式,網路地址轉換(Network Address Transformation, NAT)模式和主機(Host Only)模式 下面將簡單的介紹

Linux無線網路設定(wpa_supplicant的使用)

主機環境:Gentoo Linux 3.1.10WPA Supplicant工具包可以讓您連線到那些使用WPA的AP。因為還只是beta版,所以它的配置方法仍會常常變化——儘管如此,在大部分情況下它已經能很好的工作。安裝上wap_supplicant後可以通過修改/etc/w

Kali-Linux無線網路滲透測試-李亞偉-第3章-監聽WiFi網路--虛擬機器使用無線網絡卡

如果要管理無線網絡卡,則首先需要將該網絡卡插入到系統中。當用戶在物理機中使用無線網絡卡時,可能直接會被識別出來。如果是在虛擬機器中使用的話,可能無法直接連線到虛擬機器的作業系統中。這時候使用者需要斷開該網絡卡與物理機的連線,然後選擇連線到虛擬機器。在虛擬機器中只支援USB

VirtualBox 設定虛擬機器網路 以及內建的linux系統 網路靜態ip的配置 包含有線和無線兩種方式

我們討論一下如何在Linux中設定網路連線,配置網路連線最基本的涉及到IP地址、掩碼、閘道器和DNS設定,一般情況下系統設定好以上資訊後就可以接入網路了,這裡介紹了Linux在文字模式下通過修改配置檔案來配置基本網路連線,涉及到更多的網路配置方法請參閱相關的書籍,本文只供Linu

無線通訊網路學習之LTE網路架構篇(20141208)

今天來學習一下LTE的網路架構: 1.LTE網路架構簡化了既有通訊網路架構,並可以與其他IP網路進行通訊的無縫整合,使其成為扁平化的全IP網路架構(Falt-All-IP); 2.改網路主要由EPC(核心網)與E-UTRAN組成,通過其他傳輸介質接入其他通訊網路,如下圖所示

史上最詳細的Kali Linux破解Wifi無線網路教程

有人說,我會用WiFi萬能鑰匙就是懂破解了?這想法的確是有點天真,如果說出去可能會被人取笑。首先今天給大家講解下Wifi破解的原理。1、Wifi萬能鑰匙的工作原理是共享收集比如A裝了萬能鑰匙,然後連線了路由一,那麼這時A手機的萬能鑰匙就會記錄該路由的資訊,如地址,帳號,密碼等

Linux上配置無線網路

wpa_cli status 導讀 iwconfig是Linux Wireless Extensions(LWE)的使用者層配置工具之一。LWE是Linux下對無線網路配置的工具,包括核心的支援、使用者層配置工具和驅動介面的支援三部分。目前很多無線網絡卡都支援LWE,

LINUX環境下怎樣設定無線網路配置

在LINUX環境下怎樣設定無線網路配置 iwconfig iwconfig是Linux Wireless Extensions(LWE)的使用者層配置工具之一。LWE是Linux下對無線網路配置的

Linux c==網路程式設計的理論知識-C/S和B/S架構和區別和選擇

C/S和B/S架構和區別和選擇 區別: 硬體環境不同: C/S 一般建立在專用的網路上, 小範圍裡的網路環境, 區域網之間再通過專門伺服器提供連線和資料交換服務. B/S 建立在廣域網之上的, 不必是專門的網路硬體環境,例與電話上網, 租用裝置. 資訊自己管理. 有比C

無線網路管理工具(linux

This is the list of available known wireless managers you can use in distributions NetworkManager - GUI based connection manager with

OEL / RedHat linux 配置無線網路連線(含驅動安裝)

本機環境:Thinkpad E440,Oracle Linux Server release 6.7(kernel 3.8.13-68.3.4.el6uek.x86_64)本機無線網絡卡為RTL8723BE,若不知道自己的無線網絡卡型別,可使用以下的命令檢視:lspci |

(筆記)Linux網路程式設計,採用TCP協議實現的C/S架構

TCP/UDP介紹 TCP(Transfer Control Protocol)傳輸控制協議是一種面向連線的協議, 當我們的網路程式使用這個協議的時候,可以保證我們的客戶端和服務端的通訊是可靠的,安全的,適合於傳輸大批量資料的情況. UDP(User Da

Linux系統目錄架構

cpu信息 bsp alt .cn .com cpu 系統 查看 內存   這一篇總結的是Linux系統系統的目錄架構,了解文件系統中各個目錄的功能。 Linux系統的目錄架構 整個是一個倒置的樹狀結構。 實踐: 1,查看CPU信息

Linux運維架構師課程 - 門徒班》【招生中】

linux運維課程簡介 阿良的課程內容主要以企業核心技術為講解對象,避免過多在企業中很少用的技術,從而減少學習負擔,這樣就可以把精力主要花費在更重要的技術上, 而不像其他培訓機構那樣,講很多高大上的技術名詞,其中可能50%的知識在工作中都用不到,學員抓不住重點,時間長了就忘了。 所以,阿良的教學模

linux的LNMP架構介紹、MySQL安裝、PHP安裝

gif rul 錯誤 snap status fpm target header .net LNMP架構介紹 和LAMP唯一不同的是,LNMP中的N指的是Nginx(類似於Apache的一種web服務軟件)。目前這種環境的應用也非常多。Nginx設計的初衷是提供一種快速、

Linux集群架構

Linux集群架構Linux集群架構 集群介紹 keepalived介紹 用keepalived配置高可用 負載均衡集群介紹 LVS介紹 LVS的調試算法 LVS NAT模式搭建 LVS DR模式搭建 keepalived LVS 集群介紹 根據功能劃分為兩大類:高可用和負載均衡高可用集群常為兩臺服務器

33 linux集群架構

linux 集群架構 簡介根據功能劃分為兩大類:高可用和負載均衡高可用集群通常為兩臺服務器,一臺工作,另外一臺作為冗余,當提供服務的機器宕機,冗余將接替繼續提供服務 負載均衡集群,需要有一臺服務器作為分發器,它負責把用戶的請求分發給後端的服務器處理,在這個集群裏,除了分發器外,就是給用戶提供服務的服務器了,這

第十八章 Linux集群架構

linux18.1 集群介紹集群概述根據功能劃分為兩大類:高可用和負載均衡.1)高可用集群通常為兩臺服務器,一臺工作,另外一臺作為冗余,當提供服務的機器宕機,冗余將接替繼續提供服務實現高可用的開源軟件有:heartbeat、keepalived。後者好用,前者好久未更新了。2)負載均衡集群,需要有一臺服務器

Linux集群架構(1)集群介紹、keepalived介紹、用keepalived配置高可用集群

高可用keepalived 集群介紹 這些實現高可用和負載均衡的都是軟件,是為我們在服務器上的服務所作用的。 keepalived介紹 用keepalived配置高可用集群在nginx上實現高可用: 準備工作:準備兩臺機器133和