《Linux协议栈源码分析》读书报告.docx
- 文档编号:3427457
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:12
- 大小:23.69KB
《Linux协议栈源码分析》读书报告.docx
《《Linux协议栈源码分析》读书报告.docx》由会员分享,可在线阅读,更多相关《《Linux协议栈源码分析》读书报告.docx(12页珍藏版)》请在冰豆网上搜索。
《Linux协议栈源码分析》读书报告
读书报告
题目《Linux协议栈源码分析》
一、介绍
1.1、中断模型
Linux支持CPU的外部硬件中断和内部中断。
不管是内部还是外部中断,系统都会根据接收到的中断信息,查询idt表。
idt表依照中断源的位置按序组成,并对应中断服务程序(以及异常处理程序)的入口地址。
Linux系统在初始化页式虚存管理的初始化以后,便调用trap_init和init_IRQ两个函数进行中断机制的初始化。
1.1.1、软中断
内部中断包含系统调用陷入和异常。
(1)系统调入陷入
系统调用就是软件有计划地调用CPU提供的特殊指令,触发CPU内部产生一个中断,于是完成一次核内核外运行空间的切换。
(2)异常
异常就是软件无意的执行了一个非法指令(比如除0)从而造成CPU内部引发一次中断。
1.1.2、硬中断
外部中断特指外部设备发出的中断信号。
但这几种中断的CPU处理过程基本相同,即:
在执行完当前指令后,或在执行当前指令期间,根据中断源所提供的“中断向量”,在内存中找到相应的ISR(中断服务例程)然后调用之。
1.2、硬中断和软中断的区别
(1)软中断是执行中断指令产生的,而硬中断是由外设引发的。
(2)硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。
(3)硬中断是可屏蔽的,软中断不可屏蔽。
(4)硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。
(5)软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。
二、中断处理
2.1、中断线
每个能够产生中断的设备或者模块都会在内核中注册一个中断服务例程(ISR),当产生中断时,中断处理程序会被执行,在中断处理程序中,首先会保存中断向量号和上下文,之后执行中断线对应的中断服务例程。
对于CPU来说,中断线是非常宝贵的资源,而由于计算机的发展,外部设备数量和种类越来越多,导致了中断线资源不足的情况,linux为了应对这种情况,实现了两种中断线分配方式,分别是:
共享中断线,中断线动态分配。
2.1.1、中断线分配方式
(1)共享中断线
多个设备共用一条中断线,当此条中断线发生中断时,因为不可能预先知道哪个特定的设备产生了中断,因此,这条中断线上的每个中断服务例程都会被执行,以验证是哪个设备产生的中断(一般的,设备产生中断时,会标记自己的状态寄存器,中断服务例程通过检查每个设备的状态寄存器来查找产生中断的设备),因此共享中断线的分配方式是比较常见的。
(2)中断线动态分配
一条中断线在可能使用的时刻才与一个设备驱动程序关联起来,这样一来,即使几个硬件设备并不共享中断线,同一个中断向量也可以由这几个设备在不同时刻运行。
2.1.2、特性
(1)中断处理程序正在运行时,CPU会通知中断控制器屏蔽产生此中断的中断线。
此中断线发出的信号被暂时忽略,当中断处理程序结束时恢复此中断线。
(2)在中断服务例程的设计中,原则上是立即处理紧急的操作,将非紧急的操作延后处理(交给软中断进行处理)。
(3)中断处理程序是运行在中断上下文,但是其是代表进程运行的,因此它所代表的进行必须处于TASK_RUNNING状态,否则可能出现僵死情况,因此在中断处理程序中不能执行任何阻塞过程。
2.2、硬中断处理
2.2.1、硬中断的开关
简单禁止和激活当前处理器上的本地中断:
local_irq_disable();
local_irq_enable();
保存本地中断系统状态下的禁止和激活:
unsignedlongflags;
local_irq_save(flags);
local_irq_restore(flags);
2.2.2、注册软中断处理函数
设备驱动程序要处理硬件中断,必须挂接ISR,则挂接一个ISR可以用这个函数,要注意的是你在挂接ISR之前要正确的初始化你的设备,并且要保证用正确的顺序挂接中断。
里面调用 setup_irq 就是把创建的irqaction{}挂接到对应中断的链表上,以至于handle_IRQ_event能根据irq号直接找到对应handler。
/*Request_irq-allocateaninterruptline
Irq:
要申请的中断号
Handler:
就是传说中的ISR
Irqflags:
中断类型标志
Devname:
该设备的名字,可以由人看懂的字符
Dev_id:
似乎是一个ID,但是实际上在我们要讨论的网络设备中就是net_device()结构的一个指针
Flags:
SA-SHIRQ中断是共享的
SA_INTERRUPI需要禁止本地中断
*/intrequest_irq(unsignedintirq,
irqreturn_t(*handler)(int,void*,structpt_regs*),
undignedlongirqflags,
constchar*devname,
void*dev_id){
intretval;
atructirqaction*action;
……
action=(structirqaction*)
Kmalloc(sizeof(structirqaction),GFR_ATOMIC);
……
此handler就是刚才介绍的handle_IRQ_event函数中要处理的handler。
action->handler=handler;
action->flags=irqflags;
action->maks-0;
action->name=devname;
action->next=NULL;
action->dev_id=dev_id;
retval=setup_irq(irq,action);
returnretval;
}
2.3、软中断处理
2.3.1、软中断的开关
禁止下半部,如softirq、tasklet和workqueue等:
local_bh_disable();
local_bh_enable();
需要注意的是,禁止下半部时仍然可以被硬中断抢占。
软中断由softirq_action结构体表示:
struct softirq_action {
void (*action) (struct softirq_action *); /* 软中断的处理函数 */ };
2.3.2、注册软中断处理函数
/** * @nr:
软中断的索引号
* @action:
软中断的处理函数
*/void open_softirq(int nr, void (*action) (struct softirq_action *)) {
softirq_vec[nr].action = action; }
例如:
open_softirq(NET_TX_SOFTIRQ,net_tx_action);
open_softirq(NET_RX_SOFTIRQ,net_rx_action);
2.3.3、待处理的软中断检查和执行
1.从一个硬件中断代码处返回时
2.在ksoftirqd内核线程中
3.在那些显示检查和执行待处理的软中断的代码中,如网络子系统中
2.4、软中断处理和硬中断处理区别
软件中断的处理方式和硬件处理路径是完全不一样的,它不必经过do_IRQ这个函数,而是直接跳转到内核中的代码执行sys_socketcall。
实际上软中断是在处理完所有中断之后才会处理的。
而且处理软中断的时候还是处于中断上下文中。
2.5、关于中断上下文的一些宏
IRQ_INPROGRESS:
当前还在中断上下文中
IRQ_DISABLED:
中断被禁止
IRQ_PENDING:
中断被挂住
IRQ_REPLAY:
继续中断处理
IRQ_AUTODETECT:
自动检测中断请求
IRQ_WAITING:
对于自动监测中断,此时可能还没有看到中断到来
IRQ_LEVEL:
使用中断优先级别——Linux没有使用这项特性
IRQ_MASKED:
该中断被屏蔽了,将来不希望看到
IRQ_PER_CPU:
每个CPU都有一个IRQ
三、中断处理中数据结构
3.1、中断描述符
中断描述符用于描述IRQ线的属性与状态,每个IRQ都有它自己的中断描述符,这些中断描述符用一个数组保存,这个数组就是中断描述符数组,整个中断描述符数组长度为NR_IRQS(通常为224)项。
当产生一个中断或者异常时,首先会从中断描述符表中获取到一个中断向量号时(此中断向量号有可能表示中断,也可能表示的是一个异常),如果是一个中断导致的,会执行do_IRQ()函数,而在do_IRQ()函数中,会根据中断向量号,从中断描述符数组中获取对应的中断描述符,如下图:
整个中断描述符结构如下:
structirq_desc{
structirq_datairq_data;/*irq的统计信息,在proc中可查到*/
unsignedint__percpu*kstat_irqs;
/*回调函数,当此中断产生中断时,会调用handle_irq,在handle_irq中进行遍历irqaction链表
*handle_simple_irq用于简单处理;
*handle_level_irq用于电平触发中断的流控处理;
*handle_edge_irq用于边沿触发中断的流控处理;
*handle_fasteoi_irq用于需要响应eoi的中断控制器;
*handle_percpu_irq用于只在单一cpu响应的中断;
*handle_nested_irq用于处理使用线程的嵌套中断;
*/irq_flow_handler_thandle_irq;
#ifdef
CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_tpreflow_handler;
#endif/*中断服务例程链表*/
structirqaction*action;/*IRQactionlist*//*状态*/
unsignedintstatus_use_accessors;
/*函数调用中使用,另一个名称为istate*/
unsignedintcore_internal_state__do_not_mess_with_it;
/*嵌套深度,中断线被激活显示0,如果为正数,表示被禁止次数*/
unsignedintdepth;/*nestedirqdisables*/
unsignedintwake_depth;/*nestedwakeenables*/
/*此中断线上发生的中断次数*/
unsignedintirq_count;/*FordetectingbrokenIRQs*/
/*上次发生未处理中断时的jiffies值*/
unsignedlonglast_unhandled;/*Agingtimerforunhandledcount*/
/*中断线上无法处理的中断次数,如果当第100000次中断发生时,有超过99900次是意外中断,系统会禁止这条中断线*/
unsignedintirqs_unhandled;
atomic_tthreads_handled;
intthreads_handled_last;
/*锁*/
raw_spinlock_tlock;
structcpumask*percpu_enabled;
#ifdefCONFIG_SMP
/*CPU亲和力关系,其实就是每个CPU是占一个bit长度,某CPU上置为1表明该CPU可以进行这个中断的处理*/
conststructcpumask*affinity_hint;
structirq_affinity_notify*affinity_notify;
#ifdefCONFIG_GENERIC_PENDING_IRQ
/*用于调整irq在各个cpu之间的平衡*/
cpumask_var_tpending_mask;
#endif
#endif
unsignedlongthreads_oneshot;
atomic_tthreads_active;
/*用于synchronize_irq(),等待该irq所有线程完成*/
wait_queue_head_twait_for_threads;
#ifdefCONFIG_PM_SLEEP
/*irqaction数量*/
unsignedintnr_actions;
unsignedintno_suspend_depth;
unsignedintforce_resume_depth;
#endif
#ifdefCONFIG_PROC_FS
/*指向与IRQn相关的/proc/irq/n目录的描述符*/
structproc_dir_entry*dir;
#endif
intparent_irq;
structmodule*owner;
/*在/proc/interrupts所显示名称*/
constchar*name;
}____cacheline_internodealigned_in_smp;
core_internal_state__do_not_mes_with_it成员是用于记录此中断线状态的,中断线状态有如下几种形式:
IRQS_AUTODETECT /*该IRQ线用来进行硬件设备探测*/
IRQS_SPURIOUS_DISABLED//该IRQ线被禁止,是由于产生了欺骗性中断
IRQS_POLL_INPROGRESS/*该IRQ进行轮询检查是否发生中断*/
IRQS_ONESHOT /*此IRQ没有在主处理函数中进行unmasked处理*/
IRQS_REPLAY /*IRQ线已被禁止,但前一个出现的中断还没有被应答*/
IRQS_WAITING /*进行硬件设备探测时,会将所有没有挂载中断服务程序的IRQ线状态设置为IRQS_WAITING,如果该IRQ上有中断产生,就清除这个状态,可以推断哪些引脚产生过中断*/
IRQS_PENDING /*IRQ已经被应答(挂起),但是内核还没有进行处理*/
IRQS_SUSPENDED /*此IRQ被延迟*/
3.2、中断描述符表和中断描述符数组
在中断系统中有两个名字很相像的结构,就是中断描述符表和中断描述符数组。
这里我们先说说中断描述符表。
一个系统中的中断和异常加起来一共是256个,它们以向量的形式保存在中断描述符表中,每一个向量是8字节(整个表大小就是8x256=2048字节),其主要保存着权限位和向量对应的中断或异常处理程序的入口地址。
而一般的,linux会将中断描述符表中的0~31用于非屏蔽中断和异常,其他的中断用于32~255之间。
CPU把中断描述符表的向量类型分为三种类型:
任务门、中断门、陷阱门。
CPU为了防止恶意程序访问中断,限制了中断门的权限,而在某些时候,用户程序又必须使用中断,所以Linux把中断描述符的中断向量类型改为了5种:
中断门,系统门,系统中断门,陷阱门,任务门。
这个中断描述符表的基地址保存在idtr寄存器中。
(1)中断门
用户程序不能访问的CPU中断门(权限字段为0),所有的中断处理程序都是这个,被限定在内核态执行。
会清除IF标志,屏蔽可屏蔽中断。
(2)系统门
用户程序可以访问的CPU陷阱门(权限字段为3)。
我们的系统调用就是通过向量128系统门进入的。
(3)系统中断门
能够被用户进程访问的CPU陷阱门(权限字段为3),作为一个特别的异常处理所用。
(4)陷阱门
用户进程不能访问的CPU陷阱门(权限字段为0),大部分异常处理程序入口都为陷阱门。
(5)任务门
用户进程不能访问的CPU任务门(权限字段为0),''Doublefault"异常处理程序入口。
3.3、中断控制器描述符(PIC、APIC)
为了方便说明,这里我们将PIC和APIC统称为中断控制器。
中断控制器是作为中断(IRQ)和CPU核之间的一个桥梁而存在的,每个CPU内部都有一个自己的中断控制器,中断线并不是直接与CPU核相连,而是与CPU内部或外部的中断控制器相连。
叫做可编程中断控制器的原因是其本身有一定的寄存器,CPU可以通过操作设置中断控制器屏蔽某个中断引脚的信号,实现硬件上的中断屏蔽。
中断控制器也可以级联提供更多的中断线,
CPU的INTR与中断控制器的INT相连,INTA与ACK相连,当一个外部中断发生时(比如键盘中断IRQ1),中断控制器与CPU交互操作如下:
IRQ1发生中断,主中断控制器接收到中断信号,检查中断屏蔽寄存器IRQ1是否被屏蔽,如果屏蔽则忽略此中断信号。
将中断控制器中的中断请求寄存器对应的IRQ1位置位,表示收到IRQ1中断。
中断控制器拉高INT引脚电平,告知CPU有中断发生。
CPU每执行完一条指令时,都会检查INTR引脚是否被拉高,这里已被拉高。
CPU检查EFLAGS寄存器的中断运行标志位IF是否为1,若为1,表明允许中断,通过INTA向中断控制器发出应答。
中断控制器接收到应答信号,将IRQ1的中断向量号发到数据总线上,此时CPU会通过数据总线读取IRQ1的中断向量号。
最后,如果中断控制器需要EOI(EndofInterrupt)信号,CPU则会发送,否则中断控制器自动将INT拉低,并清除IRQ1对应的中断请求寄存器位。
在linux内核中,用structirq_chip结构体描述一个可编程中断控制器,它的整个结构和调度器中的调度类类似,里面定义了中断控制器的一些操作,如下:
structirq_chip{
/*中断控制器的名字*/
constchar*name;
/*控制器初始化函数*/
unsignedint(*irq_startup)(structirq_data*data);
/*控制器关闭函数*/
void(*irq_shutdown)(structirq_data*data);
/*使能irq操作,通常是直接调用irq_unmask(),通过data参数指明irq*/
void(*irq_enable)(structirq_data*data);
/*禁止irq操作,通常是直接调用irq_mask,严格意义上,他俩其实代表不同的意义,disable表示中断控制器根本就不响应该irq,而mask时,中断控制器可能响应该irq,只是不通知CPU*/
void(*irq_disable)(structirq_data*data);
/*用于CPU对该irq的回应,通常表示cpu希望要清除该irq的pending状态,准备接受下一个irq请求*/
void(*irq_ack)(structirq_data*data);
/*屏蔽irq操作,通过data参数表明指定irq*/
void(*irq_mask)(structirq_data*data);
/*相当于irq_mask()+irq_ack()*/
void(*irq_mask_ack)(structirq_data*data);
/*取消屏蔽指定irq操作*/
void(*irq_unmask)(structirq_data*data);
/*某些中断控制器需要在cpu处理完该irq后发出eoi信号*/
void(*irq_eoi)(structirq_data*data);
/*用于设置该irq和cpu之间的亲和力,就是通知中断控制器,该irq发生时,那些cpu有权响应该irq*/
int(*irq_set_affinity)(structirq_data*data,conststructcpumask*dest,boolforce);
int(*irq_retrigger)(structirq_data*data);
/*设置irq的电气触发条件,例如IRQ_TYPE_LEVEL_HIGH(电平触发)或IRQ_TYPE_EDGE_RISING(边缘触发)*/
int(*irq_set_type)(structirq_data*data,unsignedintflow_type);
/*通知电源管理子系统,该irq是否可以用作系统的唤醒源*/
int(*irq_set_wake)(structirq_data*data,unsignedinton);
void(*irq_bus_lock)(structirq_data*data);
void(*irq_bus_sync_unlock)(structirq_data*data);
void(*irq_cpu_online)(structirq_data*data);
void(*irq_cpu_offline)(structirq_data*data);
void(*irq_suspend)(structirq_data*data);
void(*irq_resume)(structirq_data*data);
void(*irq_pm_shutdown)(structirq_data*data);
void(*irq_calc_mask)(structirq_data*data);
void(*irq_print_chip)(structirq_data*data,structseq_file*p);
int(*irq_request_resources)(structirq_data*data);
void(*irq_release_resources)(structirq_data*data);
unsignedlongflags;
};
3.4、中断服务例程(ISR)
中断服务例程用于描述一个设备的中断处理(区别与中断处理函数),每个申请了中断的外部设备都会有一个中断服务例程,其作用就是执行对应设备的中断处理。
当多个设备共享IRQ线时,内核会将此IRQ线上所有设备的中断服务例程组织成一个链表并保存在中断描述符中,当此IRQ线产生中断时,中断处理函数会依次执行此IRQ线上的中断服务例程。
内核使用structirqaction描述一个中断服务例程:
structirqaction{
/*此中断服务例程的中断处理函数*/
irq_handler_thandler;
/*设备ID,一般用于指向中断处理时需要的数据结构传入handler*/
void*dev_id;
/*此中断服务例程在CPU上所对应的设备ID*/
void__percpu*percpu_dev_i
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux协议栈源码分析 Linux 协议 源码 分析 读书 报告