- 論壇徽章:
- 0
|
在
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 |
|