亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1262 | 回復(fù): 0
打印 上一主題 下一主題

內(nèi)核中的TCP的追蹤分析-14-TCP(IPV4)的客戶端與服務(wù)器端socket連接過程-1 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2008-11-20 12:01 |只看該作者 |倒序?yàn)g覽


http://blog.chinaunix.net/u2/64681/showart_1404746.html
內(nèi)核中的TCP的追蹤分析-7-TCP(IPV4)的socket接收連接,那節(jié)中,我們在文章最后出現(xiàn)了一點(diǎn)錯(cuò)誤,誤將newsock當(dāng)做客戶端也就是發(fā)送連接請求方的socket來理解了,并且newsk也并非是從客戶端即發(fā)送方載運(yùn)過來的sock結(jié)構(gòu),它也是在服務(wù)器端生成的,具體生成過程我們本節(jié)將會(huì)看到,這也是我們彌補(bǔ)整個(gè)連接請求到連接接收的關(guān)鍵過程。在開始這節(jié)之前我們先將那里的newsock和newsk的錯(cuò)誤分析糾正過來,請朋友們再回去看一下,因?yàn)楸疚南祩(gè)人獨(dú)立完成,所以錯(cuò)誤在所難免。我們來看這關(guān)鍵的底層驅(qū)動(dòng)相關(guān)的過程。還是象上節(jié)一樣我們來看cs8900和dm9000的驅(qū)動(dòng)中的代碼,在cs8900的驅(qū)動(dòng)程序中,我們上一節(jié)看到了他的初始化函數(shù)cs89x0_probe()進(jìn)一步調(diào)用了cs89x0_probe1()函數(shù)
static int __init
cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
{
。。。。。。
dev->open        = net_open;
。。。。。。
retval = register_netdev(dev);
。。。。。。
}
在這個(gè)初始化過程中將網(wǎng)卡設(shè)備的open鉤子函數(shù)掛入了驅(qū)動(dòng)程序中的net_open()函數(shù)后向內(nèi)核注冊登記了cs8900的設(shè)備結(jié)構(gòu)dev。那么什么時(shí)候會(huì)執(zhí)行這個(gè)net_open()鉤子函數(shù)呢我們看一下上一節(jié)
http://blog.chinaunix.net/u2/64681/showart_1421758.html
在那篇文章中我們提到了使用ifconfig eth0 up啟動(dòng)網(wǎng)卡時(shí)會(huì)執(zhí)行系統(tǒng)調(diào)用sys_ioctl(),那里只是簡要的描述了一個(gè)大致的過程,將來有以后的對內(nèi)核的分析文章中我們會(huì)再把ioctl的系統(tǒng)調(diào)用過程詳細(xì)的描述,在那篇文章中我們看到執(zhí)行了dev_open()
int dev_open(struct net_device *dev)
{
。。。。。。
ret = dev->open(dev);
。。。。。。
}
所以會(huì)執(zhí)行鉤子函數(shù)net_open(),我們來看這個(gè)函數(shù)
static int
net_open(struct net_device *dev)
{
。。。。。。
if (request_irq(i, net_interrupt, 0, dev->name, dev) == 0)
。。。。。。
}
很明顯這里為我們的cs8900網(wǎng)卡向內(nèi)核注冊登記了一個(gè)中斷,這是在一個(gè)for循環(huán)中執(zhí)行的i變量是確定cs8900要申請的中斷數(shù)量。
如果朋友們在你的終端中查看中斷輸入以下命令
[root@/wumingxiaozu]#cat /proc/interrupts
           CPU0
30: 8122 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
34: 0 s3c I2SSDI
35: 0 s3c I2SSDO
42: 0 s3c ohci_hcd:usb1
43: 0 s3c s3c2440-i2c
53: 3884 s3c-ext eth0
70: 36 s3c-uart0 s3c2440-uart
71: 62 s3c-uart0 s3c2440-uart
83: 0 - s3c2410-wdt
Err: 0
很明顯我們的網(wǎng)卡名稱是etho他申請到了53號中斷線,并且看到他已經(jīng)產(chǎn)生的中斷次數(shù)是3884次,這里我們不詳細(xì)講述中斷線的申請過程了,以后在內(nèi)核相關(guān)的分析文章中分詳細(xì)描述2.6.26內(nèi)核的中斷流程。我們還是圍繞最重要的,這里不了解的朋友請參閱相關(guān)的資料,假設(shè)我們的中斷線申請成功后,每次網(wǎng)卡接收到數(shù)據(jù)時(shí)都會(huì)產(chǎn)生一次中斷,我們看到在調(diào)用request_irq()函數(shù)時(shí)注冊的中斷處理函數(shù)是net_interrupt(),因?yàn)槲覀兊姆治鲋攸c(diǎn)不是驅(qū)動(dòng)程序本身所以這里只集中看與我們內(nèi)核至關(guān)重要的部分
static irqreturn_t net_interrupt(int irq, void *dev_id)
{
。。。。。。
    while ((status = readword(dev->base_addr, ISQ_PORT))) {
        if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status);
        handled = 1;
        switch(status & ISQ_EVENT_MASK) {
        case ISQ_RECEIVER_EVENT:
            /* Got a packet(s). */
            net_rx(dev);
            break;
。。。。。。
}
查看中斷產(chǎn)生的原因如果是由于接收到數(shù)據(jù)產(chǎn)生的中斷則會(huì)調(diào)用net_rx()進(jìn)行處理。
static void
net_rx(struct net_device *dev)
{
    struct net_local *lp = netdev_priv(dev);
    struct sk_buff *skb;
    int status, length;
    int ioaddr = dev->base_addr;
    status = readword(ioaddr, RX_FRAME_PORT);
    length = readword(ioaddr, RX_FRAME_PORT);
    if ((status & RX_OK) == 0) {
        count_rx_errors(status, lp);
        return;
    }
    /* Malloc up new buffer. */
    skb = dev_alloc_skb(length + 2);
    if (skb == NULL) {
#if 0        /* Again, this seems a cruel thing to do */
        printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
#endif
        lp->stats.rx_dropped++;
        return;
    }
    skb_reserve(skb, 2);    /* longword align L3 header */
    readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
    if (length & 1)
        skb->data[length-1] = readword(ioaddr, RX_FRAME_PORT);
    if (net_debug > 3) {
        printk(    "%s: received %d byte packet of type %x\n",
            dev->name, length,
            (skb->data[ETH_ALEN+ETH_ALEN]  8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
    }
        skb->protocol=eth_type_trans(skb,dev);
    netif_rx(skb);
    dev->last_rx = jiffies;
    lp->stats.rx_packets++;
    lp->stats.rx_bytes += length;
}
Dm9000的代碼類同,我們這里只拿cs8900的驅(qū)動(dòng)代碼來舉例分析,我們可以從上述代碼中看到cs8900網(wǎng)卡驅(qū)動(dòng)程序分配一個(gè)數(shù)據(jù)包專用結(jié)構(gòu)變量skb,然后初始化設(shè)置后就將網(wǎng)卡的數(shù)據(jù)讀入到這個(gè)結(jié)構(gòu)中,調(diào)用netif_rx()函數(shù)向內(nèi)核通知已經(jīng)有數(shù)據(jù)接收到了。函數(shù)的其余代碼我們不分析了,與具體硬件電路相關(guān)。我們看這個(gè)netif_rx()
int netif_rx(struct sk_buff *skb)
{
    struct softnet_data *queue;
    unsigned long flags;
    /* if netpoll wants it, pretend we never saw it */
    if (netpoll_rx(skb))
        return NET_RX_DROP;
    if (!skb->tstamp.tv64)
        net_timestamp(skb);
    /*
     * The code is rearranged so that the path is the most
     * short when CPU is congested, but is still operating.
     */
    local_irq_save(flags);
    queue = &__get_cpu_var(softnet_data);
    __get_cpu_var(netdev_rx_stat).total++;
    if (queue->input_pkt_queue.qlen = netdev_max_backlog) {
        if (queue->input_pkt_queue.qlen) {
enqueue:
            dev_hold(skb->dev);
            __skb_queue_tail(&queue->input_pkt_queue, skb);
            local_irq_restore(flags);
            return NET_RX_SUCCESS;
        }
        napi_schedule(&queue->backlog);
        goto enqueue;
    }
    __get_cpu_var(netdev_rx_stat).dropped++;
    local_irq_restore(flags);
    kfree_skb(skb);
    return NET_RX_DROP;
}
這個(gè)函數(shù)位于/net/core/dev.c中的1790行處,函數(shù)中出現(xiàn)了一個(gè)我們還未曾接觸過的數(shù)據(jù)結(jié)構(gòu)
struct softnet_data。{
    struct net_device    *output_queue;
    struct sk_buff_head    input_pkt_queue;
    struct list_head    poll_list;
    struct sk_buff        *completion_queue;
    struct napi_struct    backlog;
#ifdef CONFIG_NET_DMA
    struct dma_chan        *net_dma;
#endif
};
從名稱上看是“軟中斷數(shù)據(jù)”這個(gè)結(jié)構(gòu)其實(shí)是專門用于中斷概念中的“下半部”,也就是關(guān)鍵的中斷是我們的CPU在關(guān)閉外部中斷防止其他干擾情況下處理的“上半部”,但是了解中斷朋友們知道中斷不能關(guān)閉的時(shí)間太長,但是我們的數(shù)據(jù)還需要繼續(xù)處理就需要放在“下半部”中斷過程中來完成,這些概念我們將在以后的中斷章節(jié)描述,我們在上面的函數(shù)中看到首先聲明了這么一個(gè)“軟中斷”變量queue,接著我們看到
    queue = &__get_cpu_var(softnet_data);
從每個(gè)cpu的全局結(jié)構(gòu)中取得這個(gè)結(jié)構(gòu),這是什么時(shí)候初始化的呢?我們在/include/linux/netdevice.h頭文件中看到
DECLARE_PER_CPU(struct softnet_data,softnet_data);
我們看到引用的宏DECLARE_PER_CPU在/include/linux/percpu.h中11行處
#ifdef CONFIG_SMP
#define DEFINE_PER_CPU(type, name)                    \
    __attribute__((__section__(".data.percpu")))            \
    PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name
這個(gè)宏在使用smp多cpu的時(shí)候有效,這里有一個(gè)名詞per-CPU 變量,它是一個(gè)有趣的 2.6 內(nèi)核特性,使用smp架構(gòu)時(shí)創(chuàng)建一個(gè)per-CPU變量后,smp系統(tǒng)中每個(gè)處理器都會(huì)獲得該變量的副本。它的優(yōu)點(diǎn)就是對per-CPU變量的訪問(幾乎)不需要加鎖,因?yàn)槊總(gè)處理器都使用自己的副本。per-CPU 變量也可存在于它們各自的處理器緩存中,這就在頻繁更新時(shí)帶來了更好性能。關(guān)于smp的話題有很多資料,我們也會(huì)在將來分析和探討。
我們既然看到這里要取得這個(gè)per_cpu的變量softnet_data,他肯定是系統(tǒng)初始化時(shí)進(jìn)行了相關(guān)的初始化操作。首先請朋友看一下
http://blog.chinaunix.net/u2/64681/showart_1385994.html
那節(jié)中我們看到了如何使系統(tǒng)執(zhí)行net_dev_init()網(wǎng)絡(luò)的初始化函數(shù),softnet_data就是在那里初始化的,我們看一下相關(guān)的代碼
static int __init net_dev_init(void)
{
。。。。。。
    for_each_possible_cpu(i) {
        struct softnet_data *queue;
        queue = &per_cpu(softnet_data, i);
        skb_queue_head_init(&queue->input_pkt_queue);
        queue->completion_queue = NULL;
        INIT_LIST_HEAD(&queue->poll_list);
        queue->backlog.poll = process_backlog;
        queue->backlog.weight = weight_p;
    }
。。。。。。
}
這里對每個(gè)cpu的softnet_data這樣的per_cpu變量都進(jìn)行了初始化,包括接收數(shù)據(jù)包隊(duì)列input_pkt_queue和輪詢隊(duì)列poll_list,尤其最重要是設(shè)置了輪詢的函數(shù)process_backlog我們在下邊的分析中用到這個(gè)函數(shù)。
回到我們上邊的netif_rx()函數(shù)中,這里我們看們到具體的cpu取得這個(gè)結(jié)構(gòu),這個(gè)結(jié)構(gòu)中保存著網(wǎng)絡(luò)數(shù)據(jù)包的隊(duì)列頭,我們可以netif_rx函數(shù)中將cs8900的數(shù)據(jù)包調(diào)用__skb_queue_tail()掛入到這個(gè)結(jié)構(gòu)input_pkt_queue隊(duì)列的尾部。然后調(diào)用napi_schedule()函數(shù)。
static inline void napi_schedule(struct napi_struct *n)
{
    if (napi_schedule_prep(n))
        __napi_schedule(n);
}
void __napi_schedule(struct napi_struct *n)
{
    unsigned long flags;
    local_irq_save(flags);
    list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);
    local_irq_restore(flags);
}
這里注意struct napi_struct是用于網(wǎng)卡在中斷中的輪詢結(jié)構(gòu)。用于后半部中斷使用。
struct napi_struct {
    /* The poll_list must only be managed by the entity which
     * changes the state of the NAPI_STATE_SCHED bit. This means
     * whoever atomically sets that bit can add this napi_struct
     * to the per-cpu poll_list, and whoever clears that bit
     * can remove from the list right before clearing the bit.
     */
    struct list_head    poll_list;
    unsigned long        state;
    int            weight;
    int            (*poll)(struct napi_struct *, int);
#ifdef CONFIG_NETPOLL
    spinlock_t        poll_lock;
    int            poll_owner;
    struct net_device    *dev;
    struct list_head    dev_list;
#endif
};
我們看到這個(gè)結(jié)構(gòu)從上面調(diào)用napi_schedule()函數(shù)時(shí)傳遞下來的是&queue->backlog,也就是全局的softnet_data結(jié)構(gòu)中的backlog,我們看到上面代碼中通過
    list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);
將softnet_data的中的這個(gè)backlog鏈頭掛入到了它的poll_list隊(duì)列中。然后調(diào)用__raise_softirq_irqoff()觸發(fā)軟中斷函數(shù)net_rx_action()這個(gè)過程我們放在中斷章節(jié)中講解軟中斷,這樣內(nèi)核在適當(dāng)?shù)臅r(shí)機(jī)一般是空閑時(shí)會(huì)執(zhí)行統(tǒng)一的do_softirq()軟中斷處理函數(shù)來執(zhí)行這里已經(jīng)觸發(fā)的net_rx_action()。觸發(fā)了軟中斷以后上半部就執(zhí)行完成了,驅(qū)動(dòng)程序恢復(fù)中斷后,cs8900網(wǎng)卡繼續(xù)接收下一個(gè)數(shù)據(jù)包了,難道這樣就完了?下半部也就是在處理軟件中斷過程中會(huì)對我們剛才鏈入到softnet_data中的數(shù)據(jù)包進(jìn)行處理。但是上面netif_rx()函數(shù)中我們應(yīng)該注意那里只有在
queue->input_pkt_queue.qlen = netdev_max_backlog
也就是規(guī)定接收包的長度內(nèi)才會(huì)將數(shù)據(jù)包掛入并觸發(fā)軟中斷。我們跳過軟中斷總函數(shù)do_softirq()直接到達(dá)網(wǎng)卡的接收軟中斷函數(shù)net_rx_action()繼續(xù)往下看,關(guān)于軟中斷的概念也暫時(shí)放在以后,朋友們可以自行先查看一下介紹。這個(gè)函數(shù)在/net/core/dev.c中的2188行處
static void net_rx_action(struct softirq_action *h)
{
    struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
    unsigned long start_time = jiffies;
    int budget = netdev_budget;
    void *have;
    local_irq_disable();
    while (!list_empty(list)) {
        struct napi_struct *n;
        int work, weight;
        /* If softirq window is exhuasted then punt.
         *
         * Note that this is a slight policy change from the
         * previous NAPI code, which would allow up to 2
         * jiffies to pass before breaking out. The test
         * used to be "jiffies - start_time > 1".
         */
        if (unlikely(budget = 0 || jiffies != start_time))
            goto softnet_break;
        local_irq_enable();
        /* Even though interrupts have been re-enabled, this
         * access is safe because interrupts can only add new
         * entries to the tail of this list, and only ->poll()
         * calls can remove this head entry from the list.
         */
        n = list_entry(list->next, struct napi_struct, poll_list);
        have = netpoll_poll_lock(n);
        weight = n->weight;
        /* This NAPI_STATE_SCHED test is for avoiding a race
         * with netpoll's poll_napi(). Only the entity which
         * obtains the lock and sees NAPI_STATE_SCHED set will
         * actually make the ->poll() call. Therefore we avoid
         * accidently calling ->poll() when NAPI is not scheduled.
         */
        work = 0;
        if (test_bit(NAPI_STATE_SCHED, &n->state))
            work = n->poll(n, weight);
        WARN_ON_ONCE(work > weight);
        budget -= work;
        local_irq_disable();
        /* Drivers must not modify the NAPI state if they
         * consume the entire weight. In such cases this code
         * still "owns" the NAPI instance and therefore can
         * move the instance around on the list at-will.
         */
        if (unlikely(work == weight)) {
            if (unlikely(napi_disable_pending(n)))
                __napi_complete(n);
            else
                list_move_tail(&n->poll_list, list);
        }
        netpoll_poll_unlock(have);
    }
out:
    local_irq_enable();
#ifdef CONFIG_NET_DMA
    /*
     * There may not be any more sk_buffs coming right now, so push
     * any pending DMA copies to hardware
     */
    if (!cpus_empty(net_dma.channel_mask)) {
        int chan_idx;
        for_each_cpu_mask(chan_idx, net_dma.channel_mask) {
            struct dma_chan *chan = net_dma.channels[chan_idx];
            if (chan)
                dma_async_memcpy_issue_pending(chan);
        }
    }
#endif
    return;
softnet_break:
    __get_cpu_var(netdev_rx_stat).time_squeeze++;
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);
    goto out;
}
函數(shù)很大但是也有大量的注釋,我們從頭來看,
list_head *list = &__get_cpu_var(softnet_data).poll_list;
首先是取得softnet_data的隊(duì)列頭,然后進(jìn)入一個(gè)wile循環(huán)中檢查所有輪詢結(jié)構(gòu),也就是取得我們在上半部中插入到softnet_data中的struct napi_struct,這里是根據(jù)
n = list_entry(list->next, struct napi_struct, poll_list);
關(guān)于如何取得宿主結(jié)構(gòu)的函數(shù)list_entry()我們不介紹了,相關(guān)資料很多。
接著我們看到函數(shù)中調(diào)用
work = n->poll(n, weight);
也就是通過輪詢結(jié)構(gòu)中的poll鉤子函數(shù)來執(zhí)行輪詢,這里我們在上邊的net_dev_init()函數(shù)中看到了被設(shè)置成了process_backlog()函數(shù)。追蹤進(jìn)入這個(gè)函數(shù)看一下
static int process_backlog(struct napi_struct *napi, int quota)
{
    int work = 0;
    struct softnet_data *queue = &__get_cpu_var(softnet_data);
    unsigned long start_time = jiffies;
    napi->weight = weight_p;
    do {
        struct sk_buff *skb;
        struct net_device *dev;
        local_irq_disable();
        skb = __skb_dequeue(&queue->input_pkt_queue);
        if (!skb) {
            __napi_complete(napi);
            local_irq_enable();
            break;
        }
        local_irq_enable();
        dev = skb->dev;
        netif_receive_skb(skb);
        dev_put(dev);
    } while (++work  quota && jiffies == start_time);
    return work;
}
這段函數(shù)的代碼并不難理解,在一個(gè)do_while循環(huán)中依次取出在softnet_data中input_pkt_queue隊(duì)列上的數(shù)據(jù)包,調(diào)用netif_receive_skb()函數(shù)往上傳遞,也就是我們在
http://blog.chinaunix.net/u2/64681/showart_1404746.html
最后講到的與服務(wù)器端對接的目標(biāo)。
int netif_receive_skb(struct sk_buff *skb)
{
    struct packet_type *ptype, *pt_prev;
    struct net_device *orig_dev;
    int ret = NET_RX_DROP;
    __be16 type;
    /* if we've gotten here through NAPI, check netpoll */
    if (netpoll_receive_skb(skb))
        return NET_RX_DROP;
    if (!skb->tstamp.tv64)
        net_timestamp(skb);
    if (!skb->iif)
        skb->iif = skb->dev->ifindex;
    orig_dev = skb_bond(skb);
    if (!orig_dev)
        return NET_RX_DROP;
    __get_cpu_var(netdev_rx_stat).total++;
    skb_reset_network_header(skb);
    skb_reset_transport_header(skb);
    skb->mac_len = skb->network_header - skb->mac_header;
    pt_prev = NULL;
    rcu_read_lock();
    /* Don't receive packets in an exiting network namespace */
    if (!net_alive(dev_net(skb->dev)))
        goto out;
#ifdef CONFIG_NET_CLS_ACT
    if (skb->tc_verd & TC_NCLS) {
        skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
        goto ncls;
    }
#endif
    list_for_each_entry_rcu(ptype, &ptype_all, list) {
        if (!ptype->dev || ptype->dev == skb->dev) {
            if (pt_prev)
                ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = ptype;
        }
    }
#ifdef CONFIG_NET_CLS_ACT
    skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
    if (!skb)
        goto out;
ncls:
#endif
    skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
    if (!skb)
        goto out;
    skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
    if (!skb)
        goto out;
    type = skb->protocol;
    list_for_each_entry_rcu(ptype,
            &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
        if (ptype->type == type &&
         (!ptype->dev || ptype->dev == skb->dev)) {
            if (pt_prev)
                ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = ptype;
        }
    }
    if (pt_prev) {
        ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    } else {
        kfree_skb(skb);
        /* Jamal, now you will not able to escape explaining
         * me how you were going to use this. :-)
         */
        ret = NET_RX_DROP;
    }
out:
    rcu_read_unlock();
    return ret;
}
我們在這個(gè)函數(shù)中看到一個(gè)新的數(shù)據(jù)結(jié)構(gòu)struct packet_type,,我們先看一下這個(gè)結(jié)構(gòu)再分析
struct packet_type {
    __be16            type;    /* This is really htons(ether_type). */
    struct net_device    *dev;    /* NULL is wildcarded here     */
    int            (*func) (struct sk_buff *,
                     struct net_device *,
                     struct packet_type *,
                     struct net_device *);
    struct sk_buff        *(*gso_segment)(struct sk_buff *skb,
                        int features);
    int            (*gso_send_check)(struct sk_buff *skb);
    void            *af_packet_priv;
    struct list_head    list;
};
這個(gè)結(jié)構(gòu)從名稱上看是用于不同協(xié)議的數(shù)據(jù)包類型使用的,我們不管其內(nèi)部的復(fù)雜性,還是堅(jiān)持“讀到所以理解”方法。列在這里是為了讓大家查詢方便,我們看到在上邊的代碼中還出現(xiàn)了一個(gè)全局的變量ptype_all和數(shù)組ptype_base[]。我們看到關(guān)于其的輪詢操作,這二個(gè)全局變量結(jié)構(gòu)在系統(tǒng)初始化過程被設(shè)置過了。結(jié)合
http://blog.chinaunix.net/u2/64681/showart_1358880.html
那節(jié)對socket的初始化過程函數(shù)inet_init()為什么會(huì)執(zhí)行這個(gè)函數(shù)的過程那篇文章講的已經(jīng)很詳細(xì)了,我們看到這個(gè)函數(shù)
static int __init inet_init(void)
{
。。。。。。
dev_add_pack(&ip_packet_type);
。。。。。。
}
這里的ip_packet_type是ip數(shù)據(jù)包類型結(jié)構(gòu)
static struct packet_type ip_packet_type = {
    .type = __constant_htons(ETH_P_IP),
    .func = ip_rcv,
    .gso_send_check = inet_gso_send_check,
    .gso_segment = inet_gso_segment,
};
而dev_add_pack()則將其與ptype_all聯(lián)系起來
void dev_add_pack(struct packet_type *pt)
{
    int hash;
    spin_lock_bh(&ptype_lock);
    if (pt->type == htons(ETH_P_ALL))
        list_add_rcu(&pt->list, &ptype_all);
    else {
        hash = ntohs(pt->type) & PTYPE_HASH_MASK;
        list_add_rcu(&pt->list, &ptype_base[hash]);
    }
    spin_unlock_bh(&ptype_lock);
}
很顯然將ip_packet_type的鏈頭掛入到了ptype_bash雜湊隊(duì)列中了。我們來看netif_receive_skb()函數(shù)的代碼,這里就要通過與這個(gè)結(jié)構(gòu)中的數(shù)據(jù)包類型的比對來調(diào)用deliver_skb()函數(shù)
static inline int deliver_skb(struct sk_buff *skb,
             struct packet_type *pt_prev,
             struct net_device *orig_dev)
{
    atomic_inc(&skb->users);
    return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}
這個(gè)函數(shù)轉(zhuǎn)而調(diào)用了數(shù)據(jù)包類型結(jié)構(gòu)中的func()函數(shù),我們看到上面是注冊登記的我們的ip的數(shù)據(jù)包類型結(jié)構(gòu),它的結(jié)構(gòu)我們在上面列出了,其鉤子函數(shù).func = ip_rcv,所以轉(zhuǎn)到了ip_rcv()函數(shù)去進(jìn)一步完成接收。篇幅限制不得分解到下一篇


本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u2/64681/showart_1432417.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP