6>进程撤销
进程销毁可以通过几个事件驱动--通过正常进程结束、通过信号或是通过对exit函数的调用。不
管进程如何退出,进程的节点胡都要借助对内核函数do_exit的调用。
二、Linux进程调度
1>调度:从就绪的进程中选出最适合的一个来执行。调度策略、调度时机、调度步骤。
2>调度策略
1)SCHED_NORMAL(SCHED_OTHER):普通的分时进程
2)SCHED_FIFO:先入先出的实时进程
3)SCHED_RR:时间片轮转的实时进程
4)SCHED_BATCH:批处理进程
5)SCHED_IDEL:只在系统空闲时才能够被调度执行的进程
6)调度类的概念:
CFS调度类(在kernel/sched_fair.c中实现)用于以下调度策略:SCHED_NORNAL、
SCHED_BATCH和SCHED_IDEL。
实时调度类(在kernel/sched_rt.c中实现)用于SCHED_RR核SCHED_FIFO策略。
pick_next_task:选择下一个要运行的进程
2>调度时机
调度什么时候发生即:schedule()函数什么时候被调用。调度的方式有两种:
1)主动式
在内核中直接调用schedule()当进程需要等待资源而暂时停止运行时,会把状态置于挂起(睡眠),并主动请求调度,让出CPU。
2)被动式(抢占)
用户抢占(2.4,2.6)
用户抢占发生在:
*从系统调用返回用户空间。
*从中断处理程序返回用户空间。
内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用,此时就会发生用户抢占。
内核抢占(2.6)
*在不支持内很抢占的系统中,进程/线程一旦运行于内核空间,就可以一直执行,知道主动放弃或时间片耗尽为止。这样一些非常紧急的进程或线程将长时间得不到运行。
*在支持内核抢占的系统中,更高优先级的进程/线程可以抢占正在内核空间运行的低优先级进程/线程。
*在支持内核抢占的系统中,某些特例下时不允许内核抢占的:
1、内核正进行中断处理。
2、内核正在进行中断上下的Bottom Half(中断的底半部)处理。硬件中断返回前会执行软中断,此时任然处于中断上下文中。
3、进程正持有spinlock自旋锁、writelock/readlock读写锁等,当持有这些锁时,不应该被抢占,否则由于抢占将导致其他CPU长期不能获得锁而死等。
4、内核正在执行调度程序Scheduler。抢占的原因就是为了进行新的调度,没有理由将调度程序抢占掉再运行调度程序。
为保证Linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量preempt_count,称为内核抢占计数。这一变量被设置在进程的thread_info结构中,每当内核要进入以上几种状态时,变量preempt_count就加1,指示内核不允许抢占。每当内核从以上几种状态退出时,变量preempt_count就减1,同时进行可抢占的判断与调度。
内核抢占可能发生在:
*中断处理程序完成,返回内核空间之前。
*当内核代码再一次具有可抢占性的时候,如解锁及使能软中断等。
3)调度标志(TIF_NEED_RESCHED)
作用:
内核提供了一个need_resched标志来表明是否需要重新执行一次调度。
设置:
当某个进程耗尽它的时间片时,会设置这个标志;
当一个优先级更高的进程进入可执行状态的时候,也会设置这个标志。
3>调度步骤
1)清理当前运行中的进程;
2)选择下一个要运行的进程;(pick_next_task)
3)设置新的进程的运行切换;
4)进程上下文切换。
三、Linux系统调用
1>定义:一般情况下,用户空间是不能访问内核的。它既不能访问内核中的数据,也不能调用内核中
的函数。但系统调用是一个例外。Linux内核中设置了一组用于实现各种系统函数功能的函数,称为
系统调用,用户可以通过系统调用命令在自己的应用程序中调用它们。
系统调用和普通的函数调用非常相似,区别仅仅在于,系统调用由操作系统内核实现,运行于内核
态;而普通的函数调用由函数库或用户自己提供,运行于用户态。
2>库函数
Linux系统还提供了一些C语言函数库,这些库函数对系统调用进行了一些包装和扩展。
3>系统调用数
在2.6.36/38版内核中,共有系统调用369个。可在arch/arm/include/asm/unistd.h中找到它
们。
4>工作原理
应用程序首先用适当的值填充寄存器,然后调用一个特殊的指令跳转到内核某一固定的位置,内核根
据应用程序所填充的固定值来找到相应的函数执行。
1)适当的值
在文件include/asm/unistd.h中为每个系统调用规定了唯一的编号,这个号称为系统调用号
#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
#define __NR_exit (__NR_SYSCALL_BASE + 1)
#define __NR_fork (__NR_SYSCALL_BASE + 2)
#define __NR_read (__NR_SYSCALL_BASE + 3)
2)特殊的指令
在Intel CPU中,这个指令由中断0x80实现
在ARM中,这个指令时SWI(已经重命名为svc指令)。
3)固定的位置
在ARM体系中,应用程序跳转的固定内核位置是ENTRY(vector_swi)
4)相应的函数
内核根据应用程序传递来的系统调用号,从系统调用表sys_call_table找到相应的内核函数。
CALL(sys_restart_syscall)
CALL(sys_exit)
CALL(sys_fork_wrapper)
5>实现系统调用
向内核中添加新的系统调用,需要执行3个步骤:
1、添加新的内核函数
2、更新头文件unistd.h
3、针对这个新函数更新系统调用表calls.S
例:
1在kernel/sys.c中添加函数
asmlinkage int sys_add(int a, int b)
{
return a + b;
}/*asmlinkage:使用栈传递参数*/
2在arch/arm/include/asm/unistd.h中添加如下代码:
#define __NR_add (__NR_SYSCALL_BASE + 370)
3在arch/arm/kernel/calls.S中添加代码,指向新实现的系统调用函数:
CALL(sys_add)
4重新编译内核
四、Proc文件系统
1>定义
proc文件系统是一种在用户态检查内核状态的机制。
通过/proc/meminfo,查询当前内存使用情况。
2>子目录/文件名
apm:高级电源管理信息
bus:总线以及总线上的设备
devices:可用的设备信息
driver:已经启用的驱动程序
interrupts:中断信息
ioports:端口使用信息
version:内核版本
3>特点
1)每个文件都规定了严格的权限
可读?可写?哪个用户可读?哪个用户可写?
2)可以用文本编辑程序读取(more命令,cai命令,vi程序等等)
3)不仅可以文件,还可以有子目录
4)可以自己编写程序添加一个/proc目录下的文件。
5)文件的内容都是动态创建的,并不存在于磁盘上。
4>内核描述
struct proc_dir_entry{
............................................
read_proc_t* read_proc;
write_proc_t* write_proc;
...............
1)创建文件:
struct proc_dir_entry* create_proc_entry(const char* name, mode_t mode, struct proc_dir_entry*parent)
功能: 创建proc文件
参数:
name:创建的文件名
mode:要创建的文件的属性 默认0755
parent:这个文件的父目录
}
2)创建目录:
struct proc_dir_entry* proc_mkdir(const char* name, struct proc_dir_entry* parent)
功能:
创建proc目录
参数:
name:要创建的目录名
parent:这个目录的父目录
为了能让用户读写添加的proc文件,需要挂接上读写回调函数:
read_proc
write_proc
3)读操作:
int read_func(char* buffer,char** stat, off_t off, int count, int* peof, void* data)
参数:
buffer:把要返回给用户的信息写在buffer里,最大不超过PAGE_SIZE
stat:一般不使用
off:偏移量
count:用户要取的字节数
peof:读到文件尾时,需要把*peof置1
data:一般不使用
4)写操作
int write_func(struct file* file,const char* buffer,unsigned long count,void* data)
参数:
file:该proc文件对应的file结构,一般忽略。
buffer:待写的数据所在的位置
count:待写数据的大小
data:一般不用
5>实现一个proc文件的流程
1)调用create_proc_entry创建一个struct proc_dir_entry.
2)对创建的struct proc_dir_entry进行赋值:read_proc,mode, owner,size,write_proc等等。
实例分析:
五、Linux内核异常
常在河边走,哪有不湿鞋,内核级的程序,总有死机的时候,如果运气好,会看到一些所谓"Oops"信
息。
Oops可以看成是内核级的Segmentation Fault。应用程序如果进行了非法内存访问或执行了非法指
令,会得到Segfault信号,一般的行为是coredump,应用程序也可以自己截获Segfault信号,自行处
理。如果内核自己犯了这样的错误,则会打出Oops信息。
分析步骤:
1、错误原因提示
2、调用栈(对照反汇编代码)
3、寄存器(pc)