【LINUX内核学习笔记】

#LINUX内核学习笔记

2.进程调度
每个进程都有一个need_resched标志,这是因为访问进程描述符中的数值要比访问全局标量快。(因为current宏速度很快而且描述符通常都在高速缓存中)。
用户抢占发生的情况:
1)从系统调返回用户空间时;
2)从中断处理程序返回用户空间时。

调度程序没有办法在一个内核级的任务正在执行的时候重新调度——内核中的各任务都是以协作的形式调度的,不具备抢占性。

在2.6内核中引入抢占能力,只要重新调度是安全的,内核就可以在任何时刻抢占正在执行的任务。
如果没有持有锁,正在执行的程序就可以重新导入,也就是可以重新抢占的。

内核抢占发生在:
1)中断处理程序正在执行,且返回内核空间之前;
2)内核代码再一次具有抢占性的时候;
3)如果内核中的任务显式地调用schedule();
4)如果内核中的任务阻塞(这同样也会调用schedule())。

实时调度策略
SCHED_FIFO和SCHED_RR.

SCHED_FIFO:简单的、先入先出,不使用时间片(顺序执行)。一旦执行就会一直执行,直到阻塞或释放。
SCHED_RR:带有时间片的SCHED_FIFO。进程在耗尽事先分配给他的时间片后就不再执行。按照优先级,同一优先级的时间片耗尽后会重新被轮流调度。
3.系统调用
内核提供了用户进程与内核进行交互的接口。系统调用是用户空间访问内核的唯一手段;除异常和陷入外,唯一的合法入口。
内核只跟系统调用打交道,库函数和应用程序怎么使用怎么使用系统调用,不是内核所关心的。
系统调用号一旦分配就不能有任何变更,否则编译好的应用程序就会奔溃。一个系统被删除,他所占用的系统调用号也不允许被回收。

用户空间的程序无法直接执行内核代码。通知内核的机制是靠软中断实现的:通过引发一个异常来促使系统切换到内核状态去执行异常处理程序。

每一个系统调用都有一个明确的用途。设计的越通用越好。
系统调用在内核空间执行,必须仔细检查他们的参数是否合法有效。

接受一个用户空间的指针之前,内核必须保证:
1)进程不能哄骗内核去读内核空间的数据,指针指向的内存区属于用户空间。
2)指针指向的内存区在进程的地址空间里。
3)读、写、可执行 都得标记,进程不能绕过进程的访问权限。
内核提供两种方法完成必须的检查和内核空间与用户空间之间数据的拷贝:
copy_from_user()
copy_to_user()
在这里插入图片描述
内核在执行系统调用的时候处于进程上下文。在进程上下文中,内核可以休眠(比如阻塞或显式调用)并且可以被抢占。
foo()系统调用,无论何种配置,系统调用都必须编译到核心的内核映像中去。

系统调用通过C库支持,用户程序通过包含标准头文件并和C库链接,就可以使用系统调用。
LINUX如何实现系统调用:陷入内核,传递系统调用号和参数,执行正确的系统调用函数,并把返回值带回用户空间。

内核数据结构

最有用的几个:

  • 链表
  • 队列
  • 映射
  • 二叉树

链表
链表分为:单向链表、双向链表、环形链表(也分单向和双向)
沿链表移动只能是线性的,随机访问数据,一般不使用链表。

队列
LINUX的kfifo和大多数其他队列实现类似,提供两个主要的操作:enqueue (入队列)和dequeue(出队列)。维护两个偏移量:入口偏移和出口偏移。
队列只能顺序打印。

映射
一个映射也常称为关联数组:是一个由唯一键组成的集合,而每个键必然关联一个特定值。这种键到值的关系称为映射。

二叉树
树结构是一种能提供分层的树形数据结构。

中断与中断处理

许多处理器体系结构处理异常与处理中断的方式类似。异常在产生时必须考虑与时钟同步,异常常称为同步中断。

中断处理函数
特定的中断关联特定的中断处理函数,在响应一个特殊的中断的时候,内核会执行中断处理函数,与其他内核函数区别在于,中断处理程序是被内核调用来响应中断的,他们运行在中断上下文中,上下文执行不可阻塞。
中断上下文
中断处理程序是上半部(top half),接收中断立刻执行,只做有严格时限的工作,例如对接收的中断进行应答或者复位硬件。
能够被允许稍后完成的工作是下半部(bottom half)。
网卡的上半部:网卡接收到数据包时,内核要尽快把数据拷贝到内存,然后读取更多的数据包。

注册中断处理程序
每一个设备都有相关的驱动程序,如果设备使用中断,相应的驱动程序就注册一个中断处理程序。
注册中断处理程序使用request_irq(),<linux/interrupt.h>。

/*
 * @brief 分配一条给定的中断线
 * @param[in] irq 要分配的中断号
 * @param[in] hanler 指针,指向这个中断的实际中断处理程序,只要操作系统接收到中断,该函数就被调用
 * @param[in] flags  可以为0,也可以为其他标志
 * @param[in] name 会被proc/irq或/proc/interrupts使用
 * @param[in] dev  用于共享中断线
 *
 * 
 */
int requset_irq(unsigned int irq,
				irq_handler_t handler,
				unsigned long flags,
				const char *name,
				void *dev);

repuest_irq():在给定的中断线上注册一给定的中断处理程序。
repuest_free():如果在给定的中断线上没有中断处理程序,则注销响应的处理程序,并禁用其中断线。

repuest_irq()可能会睡眠,不能再中断上下文或者其他不允许阻塞的代码中调用这个函数。
同一中断处理程序绝不会被同时调用以处理嵌套的中断。

内核接收到一个中断后,它将依次调用在该中断线上注册的每一个处理程序。

当执行一个中断处理程序时,内核处于中断上下文中,中断上下文不可以睡眠。进程是以进程上下文的形式连接到内核中的,进程上下文可以睡眠,也可以调用调度程序。中断上下文与进程没有什么关系。

中断处理的机制:
硬件设备产生中断–>上报到中断控制器处(中断控制器决定是屏蔽还是上报,通过电信号给处理器的特定管脚发送一个信号)–》上报到处理器处(处理器可以禁止或激活中断的传递)–》处理器中断内核–》执行do_IRQ()–>>该线上是否有中断处理函数,有的话是否打断还是让其先执行–ret_from_intr()–>返回内核执行中断中的代码

对于每条中断线,内核都会跳到对应的唯一的位置,这样,内核就可以直到接收的唯一的IRQ号了。

procfs是一个虚拟文件系统,它只存在于内核内存,一般安装于/proc目录中。

内核代码一般都需要获取某种锁,防止来自其他处理器对共享数据的并发访问。

local_irq_save() 禁止中断
local_irq_restore() 中断被恢复到它原来的状态
local_irq_save()与local_irq_restore()必须用在同一函数中。

第八章 下半部和退后执行的工作

所有用于实现将工作推后执行的内核机制都叫做“下半部”机制。
下半部的起源
”bottom half“BH是全局同步的。提供一个静态创建、由32个bottom halves组成的链表。上半部通过32位整数中的一位来标识出哪个bottom half可以执行。
任务队列
内核定义一组队列,其中每个队列都包含由等待调用函数组成的链表。驱动可以把他们的下半部注册到合适的队列上去。

软中断和tasklet
软中断
软中断是一组静态定义的下半部接口,必须在编译期间就进行静态注册。有32个,可以在所有的处理器上同时执行–即使两个相同类型的。像网络这种对性能要求非常高的就必须使用软中断。
tasklet
一种基于软中断实现的灵活性强、动态创建的下半部实现机制。可通过代码进行动态注册。不同类型可以在不同处理器上执行,相同类型不能同时执行。是性能和易用性寻求平衡的产物。

当前可以用来实现推后执行的应用程序:软中断、tasklet和工作队列。

软中断是在编译期间分配的。其中包含32个该结构体数组。每个被注册的软中断都占据,最多可能32个软中断。一个软中断不会被另一个软中断抢占。唯一可以抢占软中断的是中断处理程序。
触发软中断:一个注册后的软中断必须被标记后才会执行。
内核定时器和tasklet都是建立在软中断上的。
软中断处理程序执行的时候,允许响应中断,但它自己不能休眠。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值