Linux内核设计与实现 读书笔记Word下载.docx
《Linux内核设计与实现 读书笔记Word下载.docx》由会员分享,可在线阅读,更多相关《Linux内核设计与实现 读书笔记Word下载.docx(35页珍藏版)》请在冰豆网上搜索。
拷贝当前进程创建一个子进程。
2)exec():
读取可执行文件并载入地址空间开始运行。
3)写时拷贝(copy-on-wrtie):
推迟数据拷贝,在需要写入数据时,数据才会被复制。
4)vfork():
不拷贝父进程的页表项,子进程作为父进程的一个线程在它的地址空间运行,父进程被阻塞直至子进程退出,子进程不能向地址块空间写入数据。
5线程
Linux把所有的线程都当作进程来实现。
6内核线程:
独立运行在内核中的标准进程。
内核线程没有独立的地址空间,只能在内核空间中运行,创建内核线程用kernel_thread()。
7进程终结
1)释放资源;
2)进入TASK_ZOMBIE;
3)等待wait4()。
第四章进程调度
1多任务系统
非抢占式多任务:
主动让步
抢占式多任务(preemptive):
时间片
2进程
IO消耗型:
常常阻塞
处理器消耗型:
执行代码
3动态优先级调度方法
允许调度程序根据需要加减优先级。
两组优先级范围:
1)nice值:
-20至+19,默认值为0,nice值越大,优先级越低。
2)实时优先级:
0至99,任何实时进程优先级都高于普通进程。
4时间片
默认时间片为20ms。
进程时间片用完——进程到期——所有进程都到期——重新计算时间片
5可执行列队(runqueue):
每个处理器一个的可执行进程链表,还包含每个处理器的调度信息。
cpu_rq(processor):
返回给定处理器的可执行队列指针。
this_rq():
返回当前处理器的可执行队列。
task_rq(task):
返回给定任务所在的队列指针。
this_rq_lock():
锁住当前可执行列队。
rq_unlock():
释放给定列队上的锁。
★为了避免死锁,要锁住多个运行列队的代码,需要按同样的顺序获得这些锁。
6优先级数组
每个运行列队有两个优先级数组:
一个活跃的,一个过期的。
优先级数组包含一个优先级位图,共有140个优先级用了5个32位长整形保存。
活动数组:
可执行队列上的进程还有时间片剩余。
过期数组:
可执行队列上的进程耗尽了时间片。
★进程从活动数组移动至过期数组时重新计算时间片。
■重新计算时间片,以静态优先级为基础计算。
■为了判断Linux进程类型,Linux记录了进程用于休眠和执行的时间。
■若进程交互性非常强,时间片用完后,会被再次放入活动数组。
7休眠
休眠通过等待列队进程处理。
等待列队:
wait_queue_head_t
静态创建等待列队:
DECLARE_WAITQUEUE
动态创建等待列队:
init_waitqueue_head()
加入等待列队:
add_wait_queue()
移出等待列队:
remove_wait_queue()
8负载平衡
保证可执行列队之间的负载处于均衡状态(用于SMP)。
两种调用方法:
1)当前可执行列队为空;
2)系统定时器200ms一次。
9上下文切换
1)将虚拟内存从上一个进程映射到新的进程中;
2)将上个进程处理器的状态切换到新的进程,保存、恢复栈信息和寄存器信息。
10schedule()被调用的时间
1)某个进程耗尽时间片时;
2)优先级高的进程进入可执行态时;
3)返回用户空间时;
4)中断返回时。
11内核抢占
只要没有持有锁,内核就可以进行抢占。
preempt_count计数器记录内核锁的数量。
内核抢占时机:
1)从中断返回内核空间时;
2)当内核代码再一次具有可抢占性的时候;
3)内核任务显示调用shedule();
4)内核任务阻塞。
12实时性
内核实时调度策略:
1)SCHED_FIFO:
简单,先进先出调度;
2)SCHED_RR:
时间片轮转调度,耗尽时间片后不再执行。
■SCHED_NORMAL是普通、非实时的调度策略。
■内核实时调度策略是基于静态优先级的:
内核不为实时进程计算动态优先级,实时优先级范围:
0-99。
■动态非实时优先级范围:
100-139(对应nice值-20至+19)。
第五章系统调用
1应用程序通过软中断机制通知内核。
2参数验证
指针:
1)指针指向的内存区域属于用户空间;
2)指针指向的内存区域属于本进程地址空间;
3)如果读,内存标记为可读;
如果写,内存标记为可写。
3copy_to_user()的三个参数:
1)进程空间的目的地址内存;
2)内核空间源地址;
3)需要拷贝的数据长度(字节数)。
copybit_from_user()和上面相反。
第六章中断和中断处理程序
1上半部和下半部
上半部:
中断处理程序
下半部:
稍后完成的工作
2注册中断
intrequest_irq(irq,*handle,irqflag,*devname,*devid)
irq中断号。
handle一个指针,指向中断处理程序。
irqflag标志位:
SA_INTERRUPT快速中断处理程序(禁止其他中断);
SA_SHIRQ:
可在多个中断处理程序间共享中断线。
devname与中断相关的设备的ASCII文本表示法。
dev_id用于共享中断线,当中断需要退出时,dev_id提供唯一的标志信息。
从共享中断线的中断处理程序中删除指定程序。
3释放中断
free_irq(unsignedintirq,void*dev_id)
4中断处理程序
irqreturn_tintr_handle(intirq,void*dev_id,structpt_regs*regs)
regs:
一个指向结构体的指针,包含中断处理之前的处理器寄存器和状态,regs使用越来越少,应该忽略。
返回值irqreturn_t:
1)IRQ_NONE:
中断对应设备并不是注册函数期间指定的产生源;
2)IRQ_HANDLE:
正常返回值。
重入问题:
Linux的中断处理程序无需考虑重入,当一个给定的中断处理程序正在执行,相应的中断会被屏蔽。
5中断上下文
★中断上下文不可以睡眠,不能从中断上下文中调用睡眠函数。
中断栈:
现在中断处理程序拥有自己的栈,每个处理器一个,大小为4KB。
6中断控制
local_irq_disable():
禁止当前处理器上的本地中断;
local_irq_enable():
允许中断;
local_irq_save(flag):
禁止中断并保存系统状态;
local_irq_restore(flag):
恢复中断并恢复到原来的状态;
disable_irq(unsignedintirq):
禁止指定中断线;
disable_irq_nosync():
不等待当前中断处理完毕就禁止指定中断;
enable_irq(unsignedintirq):
允许指定中断;
synchronize_irq():
等待一个特定的中断处理程序的退出。
★如果调用了两次disable_irq(),需要对应调用两次enable_irq()才可以恢复。
第七章下半部和推后执行的工作
1下半部
1)缩短中断执行时间;
2)下半部执行时允许响应所有的中断。
2下半部的类型
1)BH:
早期实现,现在已经淘汰;
2)任务列队(taskqueue);
3)软中断(softirq):
静态定义的下半部接口,共32个,可在所有处理上同时执行。
4)tasklet:
基于软终端的动态下半部实现,不同类型的tasklet可在不同处理器同时执行,相同类型的tasklet不可以同时执行。
5)工作列队(workqueue):
取代任务列队,对推后执行的工作排队。
6)内核定时器:
将操作推后到某个确定的时间段。
3软中断
1)软中断最多有32个。
2)一个软中断不会抢占另外一个软中断,唯一可以抢占软中断的是中断处理程序。
3)软中断的执行时机:
(1)从一个硬件中断代码返回时;
(2)在ksoftirq内核线程中;
(3)显示检查和执行处理的软中断代码中,如网络子系统。
4)软中断保留给系统中对时间要求最严格和最重要的下半部使用:
如网络和SCSI。
4tasklet
1)tasklet通过软中断实现,本身也是软中断,有两种:
HI_SOFTIRQ(优先级较高)和TASKLET_SOFTIRQ。
2)的状态有:
0、TASKLET_STATE_SCHED(已经被调度,正等待运行)和TASKLET_STATE_RUN(正在运行,应用于多核系统)。
3)tasklet的调度过程:
(1)检查tasklet的状态是否为SCHED——>
返回;
(2)保存中断,禁止本地中断;
(3)将调度tasklet加入tasklet_vec或者tasklet_hi_vec表头;
(4)唤起TASKLET_SOFTIRQ或HI_SOFTIRQ中断——>
do_softirq();
(5)恢复中断并恢复原状态。
4)使用tasklet
(1)声明
静态创建
DECLARE_TASKLET
DECLARE_TASKLET_DISABLED
动态创建
tasklet_init(t,tasklet_handle,dev)
(2)handle
voidtasklet_handle(unsignedlongdata)
★由于tasklet靠软中断实现,所以tasklet执行中不能睡眠——>
不能使用信号量或阻塞函数。
tasklet运行时运行中断,需要做好保护工作。
(3)调度
tasklet_schedule(&
my_tasklet)加入调度列队
tasklet_disable()禁止莫个tasklet,若正在执行则等待执行完毕再禁止
tasklet_disable_nosync()立即禁止(不太安全)
tasklet_enable()激活一个tasklet
tasklet_kill()去掉列队的第一个tasklet
(4)ksoftirq辅助软中断内核线程
内核不会立即处理重新触发的软中断,当大量软中断出现时,内核会唤起一组线程来处理这些负载。
(nice=19)
5工作队列(workqueue)
1)工作队列可把工作推后,交由一个内核线程去执行。
2)工作队列在进程上下文执行,允许重新调度甚至是睡眠,但无法访问用户空间。
3)实现
工作者线程events/n,n是处理器编号。
worker_thread()函数:
执行一个死循环并休眠,当有操作,线程被唤醒。
4)使用工作队列
DECLARE_WORK(name,void(*func)(void*),void*data)
INIT_WORK(structwor