如有什么错误,请指出,大家一起学习:
1、修改进程为实时进程,linux不支持实时进程,这里的实时不是真正的实时进程,只是进程优先级比较高
修改进程优先级的函数:sched_setscheduler
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
pid:哪个进程,当前是 0
policy:调度策略,SCHED_FIFO,最高优先级
struct sched_param *param:当前策略中的优先级
修改当前策略中的较高优先级:
sched_get_priority_max 让进程在当前策略下获得最高权限
sched_get_priority_min 让进程在当前策略下获得最低权限
2、中断:
内核:统一的接口,不分平台,内核会自动根据平台选择相应的底层函数,无需个人操作硬件
内核使用的是 irq,不使用VIC,保证可移植性
内核产生 irq异常时,不往 0x18跳,而往高地址 0xffff0018 跳
mmu ---> page table -映射-> 0xffff0018(物理地址假如是 0x54000000)
由此可见,内核使用的是高端向量表,不使用 0x18是因为内核启动后的 0 地址是不去确定的,只能通过高端向量表经页表映射,分配到任意物理地址
使用高端向量表的设置:
1.设置向量表的位置
2.打开 MMU
3.查询到相应的页表
S3C6410的中断控制器:SP890(在arm里面)
64个中断源 ---> 中断号
127个外部中断 ---> 0 1 32 33 53
3、中断线:
内核响应中断 irq ---> 跳到 0xffff0018 ---> 找到中断响应函数 ---> 内核切换到 SVC 模式 --->
运行中断处理函数 ---> 扫描中断线(查看该中断源对应的中断线上注册了哪些处理函数) ---> 依次执行处理函数
内核响应的中断可以有多个处理函数,线上的函数一般是依次执行的
每条中断线上默认有一个中断函数,向线上有多个中断函数,需要中断共享
查看中断线和中断源的对应关系:
文件目录:arch/arm/mach-s3c64xx/include/mach/irqs.h
外部中断也单独分配了中断线,所以中断线的数目肯定大于中断源的数目
IRQ_EINT(x):使用它能确保平台无关,外部中断 0组,0 ~ 27
IRQ_EINT_GROUP(group, no):其它组的外部中断的申请
4、上下文(content)
与环境变量相似
PATH=/user/bin 不同的环境变量是会影响程序的执行
上下文在内核中就是程序运行的环境
进程上下文:代表进程执行的代码处在进程上下文
中断上下文:代表中断执行的代码处在中断上下文
<********- 中断上下文不能睡眠(记住) -**********>
因为:进程队列:进程调度器找 task_struct -> task_struct -> task_struct -> ...
进程睡眠后可通过进程调度器再度唤醒,但是中断没有调度器这种东西,一旦睡眠就会放弃 CPU,睡眠之后就没有什么能够唤醒中断,这样会造成内核的崩溃
进程调度器就是一个软件,运行在中断上下文中
判断上下文情况:
in_interrupt()
返回非 0 代表中断上下文
返回 0 代表进程上下文
time ls 命令
real xxx 下面的两个加上睡眠时间
user xxx 用户态执行的时间
sys xxx 命令在内核中执行的时间
5、函数:
中断函数必须包含的头文件 <linux/interrupt.h>
在linux下这些函数是跨硬件平台的
request_irq():
static inline int __must_check request_irq(unsigned int irq, \
irq_handler_t handler, unsigned long flags, \
const char *name, void *dev)
irq:中断线
handler:中断响应函数,必须短小精悍,处理完马上退出
typedef irqreturn_t (*irq_handler_t)(int, void *);
int 参数:历史遗留问题,以前作为大数组的下标使用,现在的内核已不用
void * 参数:内核会把 void * dev 指针传给这个参数。与 proc 中的 void * 传给read、write 相似
flags:中断响应方式:上升沿、、下降沿等,有相应的宏定义对应,在这个函数文件的上方
IRQF_DISABLED:禁止其它一切中断
IRQF_SAMPLE_RANDOM:将中断间隔时间假如熵池
IRQF_TIMER:给系统定时器用
IRQF_SHARED:共享中断线
name:给申请的中断线起个名字
dev:用于共享中断线,这个参数会传给中断响应函数的 void * 参数,在删除响应函数时,用于标识哪一个
free_irq():
void free_irq(unsigned int, void *)
参数1:中断线
参数2:
中断线不共享时,参数2没什么作用
中断线共享时,参数2传的是中断线的名字,释放的是某个函数
所以一般在使用的时侯,不管中断线是否共享,都传中断线的名字
local_irq_enable():
内核默认情况下,中断是打开的
local_irq_disable():
与 cpsid i 等同
内核是不允许关闭中断的,你手动关了,内核会提示你,并且自主的打开
cat /proc/interrupt 下查看已经注册的中断线的信息
中断线 中断相应的次数 VIC中断源/外部中断 中断线的名字
用中断保证某段代码不会被打断
方法一:
local_irq_disable();
...;
local_irq_enable();
但是这种方法可能会产省错误
方法二:
unsigned long flag;
local_irq_save(flag); 这个函数代表两步:1.保存状态 2.禁止中断
...;
local_irq_restore(flag); 恢复状态 恢复中断
6、中断共享:
一个中断线上可以有多个中断处理函数
在申请中断线(requert_irq),flags 位加上 IRQF_SHARED(共享) | IRQF_SAMPLE_RANDOM(熵池,两次中断响应的时间差,放在熵池中做以后的随机数使用)
第一中断函数与前面无异,申请第二个以后的函数时,要有其它规则:
irq、flags、name:必须相同
void *dev:指针必须保证与 free_irq 中的参数2 指向的是同一个地址
7、中断下半部分:
需要下半部分的原因:
中断处理函数占用的时间不能太长
中断处理函数处在中断上下文,不能阻塞(睡眠等待,区别于死等)
中断处理函数不能打断
与内核版本相关,2.6.28版本内核默认情况是可以被打断的,3.4.24是不可被打断的
申请中断时,加标志(flag):IRQF_DISABLED
在中断处理函数中应该做的:
不允许打断的
必须马上处理的
跟硬件相关的
其它的都放到下半部分
下半部分有以下几种:
软中断:(内核提供的一种机制,是在内核编译的时候就定死了,处在中断上下文)
内核编译好后就不能改了,但是可以被其它中断打断(保护、打断,执行完别的后恢复)
软中断个数有限,最多 32 个;因为永乐一个 32 位数表示每一个软中断
在中断处理函数退出后检测有无软中断挂起,如果有,依次执行软中断处理函数
同一个软中断允许在不同的 CPU 上同时执行
内核源码 <path>/include/linux/interrupt.h(423行的枚举是内核已经使用的软中断)
枚举里所列的是软中断号
struct softirq_action 每一个软中断对应这么一个结构体,内核自动生成
软中断函数源码的实现:<path>/kernel/softirq.c
irq_exit 在中断返回时执行
软中断的使用: (一般不用)
1.枚举 <path>/include/linux/interrupt.h(423行的枚举是内核已经使用的软中断)
在枚举里的 RCU_SOFTIRQ 之前添加 MY_SOFTIRQ
将在 <path>/kernel/softirq.c 中的数组 char *softirq_to_name[NR_SOFTIRQS] 中添加软中断的名字
在此文件中,将下面将会用到两个函数标号共享 EXPORT_SYMBOL(函数名)
重新编译内核‘
2.函数 open_softirq(枚举, 函数)
3.位图 raise_softirq(变量是 0 ~ 31 位),功能是将相应位置 1
tasklet:小任务机制
基于软中断实现
tasklet 处理函数在中断上下文,可以被打断
<path>/include/linux/interrupt.h 中的枚举的 HI_SOFTIRQ 和 TASKLET_SOFTIRQ 对应两个链表,每个成员都对应 struct tasklet_struct 这么一个结构体
struct tasklet_struct {
struct tasklet_struct *next;
unsigned long state;
//初始化是 0,即没有被调度,也没有被执行;调度为 1, 执行为2;下面的有枚举标识
// enum {
TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */
TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
};
atomic_t count;//标识这个tasklet能否运行;
只有count为 0 的时候,该tasklet才能被运行
void (*func)(unsigned long); //执行的函数指针
unsigned long data; //
};
同一个 tasklet 不能同时在不用的CPU上执行
同一个 tasklet 不能被重复调用
使用:
1.创建 struct tasklet_struct
利用 tasklet_init() 初始化一下
2.放进链表
tasklet_schedule 加了判断,防止链表被打断,加上检查是否被调度过
基于这个函数 __tasklet_schedule
tasklet_hi_schedule
基于这个函数 __tasklet_hi_schedule
tasklet_kill
拿掉被调度的 tasklet
以睡眠的方式等待一个 tasklet 结束,结束了才会被拿走
workqueue
核心头文件 <include/linux/workqueue.h>
基于内核线程实现的,在默认情况下每一个 cpu 核linux都会创建一个工作队列,每一个工作队列对应一个内线成,内核线程会定时运行
处理函数处于进程上下文,可以睡眠
一个内核线程对应一个工作队列
同一个工作不能重复加入工作队列,只有在工作结束后才可以再添加
两种工作(任务):
任务结构体:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
延时任务结构体:
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};
定义一个工作并初始化
DECLARE_WORK()
DECLARE_DELAYED_WORK()
初始化一个工作
INIT_WORK()
INIT_DELAYED_WORK()
把一个工作(任务)添加到工作队列
schedule_work()
schedule_delayed_work()
以睡眠的方式等待所以的工作完成
flush_schedule_work()
每一个工作队列都对应一个结构体, struct workqueue_struct
自己创建一个工作队列:
struct workqueue_struct 要通过函数创建
创建工作队列
create_workqueue()
往相应的工作队列中中加入工作
queue_work()
向相应的工作队列中添加延时的任务
queue_delayed_work()
以睡眠的额方式等待队列上的所有任务完成
flush_workqueue()
销毁一个工作队列
destroy_workqueue()
1、修改进程为实时进程,linux不支持实时进程,这里的实时不是真正的实时进程,只是进程优先级比较高
修改进程优先级的函数:sched_setscheduler
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
pid:哪个进程,当前是 0
policy:调度策略,SCHED_FIFO,最高优先级
struct sched_param *param:当前策略中的优先级
修改当前策略中的较高优先级:
sched_get_priority_max 让进程在当前策略下获得最高权限
sched_get_priority_min 让进程在当前策略下获得最低权限
2、中断:
内核:统一的接口,不分平台,内核会自动根据平台选择相应的底层函数,无需个人操作硬件
内核使用的是 irq,不使用VIC,保证可移植性
内核产生 irq异常时,不往 0x18跳,而往高地址 0xffff0018 跳
mmu ---> page table -映射-> 0xffff0018(物理地址假如是 0x54000000)
由此可见,内核使用的是高端向量表,不使用 0x18是因为内核启动后的 0 地址是不去确定的,只能通过高端向量表经页表映射,分配到任意物理地址
使用高端向量表的设置:
1.设置向量表的位置
2.打开 MMU
3.查询到相应的页表
S3C6410的中断控制器:SP890(在arm里面)
64个中断源 ---> 中断号
127个外部中断 ---> 0 1 32 33 53
3、中断线:
内核响应中断 irq ---> 跳到 0xffff0018 ---> 找到中断响应函数 ---> 内核切换到 SVC 模式 --->
运行中断处理函数 ---> 扫描中断线(查看该中断源对应的中断线上注册了哪些处理函数) ---> 依次执行处理函数
内核响应的中断可以有多个处理函数,线上的函数一般是依次执行的
每条中断线上默认有一个中断函数,向线上有多个中断函数,需要中断共享
查看中断线和中断源的对应关系:
文件目录:arch/arm/mach-s3c64xx/include/mach/irqs.h
外部中断也单独分配了中断线,所以中断线的数目肯定大于中断源的数目
IRQ_EINT(x):使用它能确保平台无关,外部中断 0组,0 ~ 27
IRQ_EINT_GROUP(group, no):其它组的外部中断的申请
4、上下文(content)
与环境变量相似
PATH=/user/bin 不同的环境变量是会影响程序的执行
上下文在内核中就是程序运行的环境
进程上下文:代表进程执行的代码处在进程上下文
中断上下文:代表中断执行的代码处在中断上下文
<********- 中断上下文不能睡眠(记住) -**********>
因为:进程队列:进程调度器找 task_struct -> task_struct -> task_struct -> ...
进程睡眠后可通过进程调度器再度唤醒,但是中断没有调度器这种东西,一旦睡眠就会放弃 CPU,睡眠之后就没有什么能够唤醒中断,这样会造成内核的崩溃
进程调度器就是一个软件,运行在中断上下文中
判断上下文情况:
in_interrupt()
返回非 0 代表中断上下文
返回 0 代表进程上下文
time ls 命令
real xxx 下面的两个加上睡眠时间
user xxx 用户态执行的时间
sys xxx 命令在内核中执行的时间
5、函数:
中断函数必须包含的头文件 <linux/interrupt.h>
在linux下这些函数是跨硬件平台的
request_irq():
static inline int __must_check request_irq(unsigned int irq, \
irq_handler_t handler, unsigned long flags, \
const char *name, void *dev)
irq:中断线
handler:中断响应函数,必须短小精悍,处理完马上退出
typedef irqreturn_t (*irq_handler_t)(int, void *);
int 参数:历史遗留问题,以前作为大数组的下标使用,现在的内核已不用
void * 参数:内核会把 void * dev 指针传给这个参数。与 proc 中的 void * 传给read、write 相似
flags:中断响应方式:上升沿、、下降沿等,有相应的宏定义对应,在这个函数文件的上方
IRQF_DISABLED:禁止其它一切中断
IRQF_SAMPLE_RANDOM:将中断间隔时间假如熵池
IRQF_TIMER:给系统定时器用
IRQF_SHARED:共享中断线
name:给申请的中断线起个名字
dev:用于共享中断线,这个参数会传给中断响应函数的 void * 参数,在删除响应函数时,用于标识哪一个
free_irq():
void free_irq(unsigned int, void *)
参数1:中断线
参数2:
中断线不共享时,参数2没什么作用
中断线共享时,参数2传的是中断线的名字,释放的是某个函数
所以一般在使用的时侯,不管中断线是否共享,都传中断线的名字
local_irq_enable():
内核默认情况下,中断是打开的
local_irq_disable():
与 cpsid i 等同
内核是不允许关闭中断的,你手动关了,内核会提示你,并且自主的打开
cat /proc/interrupt 下查看已经注册的中断线的信息
中断线 中断相应的次数 VIC中断源/外部中断 中断线的名字
用中断保证某段代码不会被打断
方法一:
local_irq_disable();
...;
local_irq_enable();
但是这种方法可能会产省错误
方法二:
unsigned long flag;
local_irq_save(flag); 这个函数代表两步:1.保存状态 2.禁止中断
...;
local_irq_restore(flag); 恢复状态 恢复中断
6、中断共享:
一个中断线上可以有多个中断处理函数
在申请中断线(requert_irq),flags 位加上 IRQF_SHARED(共享) | IRQF_SAMPLE_RANDOM(熵池,两次中断响应的时间差,放在熵池中做以后的随机数使用)
第一中断函数与前面无异,申请第二个以后的函数时,要有其它规则:
irq、flags、name:必须相同
void *dev:指针必须保证与 free_irq 中的参数2 指向的是同一个地址
7、中断下半部分:
需要下半部分的原因:
中断处理函数占用的时间不能太长
中断处理函数处在中断上下文,不能阻塞(睡眠等待,区别于死等)
中断处理函数不能打断
与内核版本相关,2.6.28版本内核默认情况是可以被打断的,3.4.24是不可被打断的
申请中断时,加标志(flag):IRQF_DISABLED
在中断处理函数中应该做的:
不允许打断的
必须马上处理的
跟硬件相关的
其它的都放到下半部分
下半部分有以下几种:
软中断:(内核提供的一种机制,是在内核编译的时候就定死了,处在中断上下文)
内核编译好后就不能改了,但是可以被其它中断打断(保护、打断,执行完别的后恢复)
软中断个数有限,最多 32 个;因为永乐一个 32 位数表示每一个软中断
在中断处理函数退出后检测有无软中断挂起,如果有,依次执行软中断处理函数
同一个软中断允许在不同的 CPU 上同时执行
内核源码 <path>/include/linux/interrupt.h(423行的枚举是内核已经使用的软中断)
枚举里所列的是软中断号
struct softirq_action 每一个软中断对应这么一个结构体,内核自动生成
软中断函数源码的实现:<path>/kernel/softirq.c
irq_exit 在中断返回时执行
软中断的使用: (一般不用)
1.枚举 <path>/include/linux/interrupt.h(423行的枚举是内核已经使用的软中断)
在枚举里的 RCU_SOFTIRQ 之前添加 MY_SOFTIRQ
将在 <path>/kernel/softirq.c 中的数组 char *softirq_to_name[NR_SOFTIRQS] 中添加软中断的名字
在此文件中,将下面将会用到两个函数标号共享 EXPORT_SYMBOL(函数名)
重新编译内核‘
2.函数 open_softirq(枚举, 函数)
3.位图 raise_softirq(变量是 0 ~ 31 位),功能是将相应位置 1
tasklet:小任务机制
基于软中断实现
tasklet 处理函数在中断上下文,可以被打断
<path>/include/linux/interrupt.h 中的枚举的 HI_SOFTIRQ 和 TASKLET_SOFTIRQ 对应两个链表,每个成员都对应 struct tasklet_struct 这么一个结构体
struct tasklet_struct {
struct tasklet_struct *next;
unsigned long state;
//初始化是 0,即没有被调度,也没有被执行;调度为 1, 执行为2;下面的有枚举标识
// enum {
TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */
TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
};
atomic_t count;//标识这个tasklet能否运行;
只有count为 0 的时候,该tasklet才能被运行
void (*func)(unsigned long); //执行的函数指针
unsigned long data; //
};
同一个 tasklet 不能同时在不用的CPU上执行
同一个 tasklet 不能被重复调用
使用:
1.创建 struct tasklet_struct
利用 tasklet_init() 初始化一下
2.放进链表
tasklet_schedule 加了判断,防止链表被打断,加上检查是否被调度过
基于这个函数 __tasklet_schedule
tasklet_hi_schedule
基于这个函数 __tasklet_hi_schedule
tasklet_kill
拿掉被调度的 tasklet
以睡眠的方式等待一个 tasklet 结束,结束了才会被拿走
workqueue
核心头文件 <include/linux/workqueue.h>
基于内核线程实现的,在默认情况下每一个 cpu 核linux都会创建一个工作队列,每一个工作队列对应一个内线成,内核线程会定时运行
处理函数处于进程上下文,可以睡眠
一个内核线程对应一个工作队列
同一个工作不能重复加入工作队列,只有在工作结束后才可以再添加
两种工作(任务):
任务结构体:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
延时任务结构体:
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};
定义一个工作并初始化
DECLARE_WORK()
DECLARE_DELAYED_WORK()
初始化一个工作
INIT_WORK()
INIT_DELAYED_WORK()
把一个工作(任务)添加到工作队列
schedule_work()
schedule_delayed_work()
以睡眠的方式等待所以的工作完成
flush_schedule_work()
每一个工作队列都对应一个结构体, struct workqueue_struct
自己创建一个工作队列:
struct workqueue_struct 要通过函数创建
创建工作队列
create_workqueue()
往相应的工作队列中中加入工作
queue_work()
向相应的工作队列中添加延时的任务
queue_delayed_work()
以睡眠的额方式等待队列上的所有任务完成
flush_workqueue()
销毁一个工作队列
destroy_workqueue()