深入理解Linux网络技术内幕——中断与网络驱动程序
接收到帧时通知驱动程序
中断处理函数
为什么有下半部函数
下半部函数解决方案
/***********************Linux-2.6.32************************************/ //include/linux/hardirq.h in_irq() //CPU正服务于硬件中断时,返回True in_softirq() //CPU正服务于软件中断时,返回True in_interrupt() //CPU正在服务于一个硬件中断或软件中断,或抢占功能关闭时,返回True //arch/x86/include/asm/hardirq.h local_softirq_pending() //本地CPU至少有一个IRQ出于未决状态时,返回True //include/linux/interrupt.h __raise_softirq_irqoff() //设置与软IRQ相关联的标识,将IRQ标记为未决 raise_softirq_irqoff() //__raise_softirq_irqoff包裹函数,当in_interrupt为False时,唤醒ksoftirqd raise_softirq() //包裹raise_softirq_irqoff,调用raise_softirq_irqoff前先关中断 //kernel/softirq.c __local_bh_enable() //开启本地CPU的下半部 local_bh_enable() //如果有任何软IRQ未决,且in_interrupt返回False,则invoke_softirq local_bh_disable() //关闭CPU下半部 //include/linux/irqflags.h local_irq_enable() //开启本地CPU中断功能 local_irq_disable() //关闭本地CPU中断功能 local_irq_save() //先把本地CPU中断状态保存,再予以关闭 local_irq_restore() //恢复本地CPU之前的中断状态,恢复local_irq_save保存的中断信息 //include/linux/spinlock.h spin_lock_bh() //取得回旋锁,关闭下半部及抢占功能 spin_unlock_bh() //释放回旋锁,重启下半部抢占功能
抢占功能
//inculde/linux/preempt.h preempt_disable() //为当前任务关闭抢占功能。可重复调用,递增引用计数器 preempt_enable() //抢占功能再度开启,(需要先检查引用计数器是否为0) preempt_enable_no_resch() //递减引用计数器,只有引用计数器为0时,抢占功能才能再度开启 preempt_check_resched() //由preempt_enable调用,检查引用计数器是否为0. // arch/x86/include/asm/thread_info.h struct thread_info { …… int preempt_count; /* 0 => preemptable, <0 => BUG */ //抢占计数器,指定进程是否能被抢占 …… };
下半部函数
旧式下半部函数(linux-2.2以前)
enum { TIMER_BH = 0, CONSOLE_BH, TQUEUE_BH, DIGI_BH, SERIAL_BH, RISCOM8_BH, SPECIALIX_BH, AURORA_BH, ESP_BH, NET_BH, //网络下半部 SCSI_BH, IMMEDIATE_BH, KEYBOARD_BH, CYCLADES_BH, CM206_BH, JS_BH, MACSERIAL_BH, ISICOM_BH };
_ _initfunc(int net_dev_init(void)) { ... ... ... init_bh(NET_BH, net_bh); ... ... ... }
extern inline void mark_bh(int nr) { set_bit(nr, &bh_active); };
skb_queue_tail(&backlog, skb); mark_bh(NET_BH); return
引入软IRQ
//include/linux/interrupt.h enum { HI_SOFTIRQ=0, //高优先级微任务 TIMER_SOFTIRQ, NET_TX_SOFTIRQ, //网络软IRQ NET_RX_SOFTIRQ, //网络软IRQ BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, //低优先级微任务软IRQ SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS };
/* * Incoming packets are placed on per-cpu queues so that * no locking is needed. */ struct softnet_data { struct Qdisc *output_queue; //qdisc是queueing discipline的简写,也就是排队规则,即qos.这里也就是输出帧的控制。 struct sk_buff_head input_pkt_queue; //当输入帧被驱动取得之前,就保存在这个队列里,(不适用与napi驱动,napi有自己的私有队列) struct list_head poll_list; //表示有输入帧待处理的设备链表。 struct sk_buff *completion_queue; //表示已经成功被传递出的帧的链表。 struct napi_struct backlog; //用来兼容非napi的驱动。 };
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; queue->backlog.gro_list = NULL; queue->backlog.gro_count = 0; } ...... }
软IRQ的注册于调度机制
// kernel/softirq.c void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; }
__raise_softirq_irqoff() //设置与软IRQ相关联的标识,将IRQ标记为未决 raise_softirq_irqoff() //__raise_softirq_irqoff包裹函数,当in_interrupt为False时,唤醒ksoftirqd raise_softirq() //包裹raise_softirq_irqoff,调用raise_softirq_irqoff前先关中断
微任务
/* * Tasklets */ struct tasklet_head { struct tasklet_struct *head; struct tasklet_struct **tail; }; static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec); static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
struct tasklet_struct { struct tasklet_struct *next; //把关联到同一个CPU的结构链接起来 unsigned long state; //位图标识,其可能的取值由TASKLET_STATE_XXX枚举 atomic_t count; //计数器,0表示微任务被关闭,不可执行。非0表示微任务已经开启 void (*func)(unsigned long); //要执行的函数 unsigned long data; //上面函数的参数 }; enum { TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ };
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。