2.1进程调度
多任务系统分为非抢占式和抢占式两种,Linux c提供抢占式多任务模式,进程在被抢占之前能够运行的时间叫做进程的时间片。在LIinux 2.6.23内核版本中,采用完全公平调度算法(CFS)代替了O(I)调度算法,因为O(I)对响应时间敏感的程序有不足。
进程分为I/O消耗型和处理器消耗型。前者指进程的大部分时间用来提交I/O请求或是等待I/O请求的;后者是指进程把事件大多数用在执行代码上。Linux更倾向于优先调度I/O消耗型进程。
Linux采用两种不同的优先级范围:第一种nice值,越大的nice意味着更低的优先级,第二种是实时优先级,其值可以配置,越高的实时优先级数值意味着进程优先级越高。任何实时进程的优先级都高于普通进程。
2.2Linux调度的实现
1)时间记账:变量vruntime可以准确的测定进程运行的时间
2)进程选择:当CFS需要选择下一个进程时,它会选择具有最小的vruntime的任务
3)调度器入口:进程调度器的入口点是schedule函数,该函数以优先级为序,从高到低依次检查每一个调度器并选择最高级别的进程。
4)睡眠和唤醒:睡眠就是进程把自己标记为休眠状态,从可执行树中移除,放入等待队列
唤醒就是进程被设置为可执行状态,从等待序列中移到可执行树中
休眠有时候会虚假唤醒,所以有时候需要一个循环处理来保证进程被唤醒的条件真正达成。
2.3 抢占和上下文切换
上下文切换就是从一个可执行进程切换到另一个可执行进程。Schedule函数主要完成两个工作:其一,把虚拟内存从上一个进程映射切换到新进程中;其二,从上一个进程的处理器状态切换到新进程的处理器状态。
内核中提供need_resched来标志表明是否需要重新执行一次调度,该标志对于内核来讲是一个信息,它表示有其他进程应当被运行了,要尽快调用调度程序。
用户抢占:内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule函数被调用,此时会发生用户抢占。用户抢占一般在如下情况下发生:其一,从系统调用返回用户空间时;其二,从中断处理程序返回用户空间时。
内核抢占:内核可以在任何时候抢占正在执行的任务。内核抢占一般在如下情况下发生:
(1)中断处理程序正在执行,且返回用户空间之前;
(2)内核代码再一次具有可抢占性的时候;
(3)如果内核中的任务显式地调用schedule函数;
(4)如果内核中的任务阻塞。
2.4进程概念
进程是正在执行的程序代码的实时结果,是处于执行期的程序以及相关的资源的总称。线程是在进程中活动的对象,内核调度的对象是线程,而不是进程。
进程提供两种虚拟机制——虚拟处理器和虚拟内存。线程之间可以共享内存,但每个都拥有各自的虚拟处理器。
进程五种状态标志:TASK_RUNNING,TASK_INTERRUPTIBLE,TASK_UN INTERRUPTIBLE,TASK_TRACED,TASK_STOPPPED.
2.5进程上下文
程序在用户空间执行时,当一个程序执行了一个系统调用或者触发了某个异常,它就陷入内核空间,称为内核“代表进程执行”并处于进程上下文中。
2.6进程与线程的创建
从现有内核线程中创建一个新的内核线程有两种方法:
(1)利用kthread_creat()函数创建,并用wake_up_process()唤醒;
(2)直接执行kthread_run().
内核线程启动之后就一直运行直到调用do_exit()退出,或者内核的其他部分调用kthread_stop()退出。
2.7孤儿进程
如果父进程在子进程之前退出,必须有一个机制来保证子进程能找到一个新父亲,否则这些孤儿进程就会在退出时永远处于僵死状态,白白耗费内存。
2.8系统调用
系统调用是用户空间访问内核的唯一手段,除异常和陷入外,它们是内核唯一的合法入口。
定义一个系统调用:
asmlinkage long sys_test(void)
内核在执行系统调用的时候处于进程上下文,在进程上下文中,内核可以休眠,并且可以被抢占。系统调用返回的时候,控制权仍在system_call中,它最终负责切换到用户空间,并让用户继续执行下去。
2.9内核设计系统调用
主要完成系统调用函数编写,系统调用号,系统调用表的填写。
步骤1:在“/include/linux/syscalls.h”中增加要添加的系统调用的声明。
asmlinkage long sys_test(void);
步骤2:在“/arch/arm/include/asm/unistd.h”
#define _NR_test(_NR_SYSCALL_BASE+361)
步骤3:在“/kernel/sys.c”中实现系统调用函数。
asmlinkage long sys_test(void)
{
printk(“pid: %d,this is test call.\n”,current->pid);
return 0;
}
步骤4:在“/arch/arm/kernek/calls.S”中加入系统调用表的初始化部分
/* 361 */ CALL(sys_test)
步骤5:生成内核镜像。应用层测试程序。
#include
#include
#include
int main()
{
int i = 10;
i = syscall(361);
printf(“after syscall : i = %d\n”,i);
return 0;
}
测试结果:
pid: 859,this is test call.
after syscall:i = 0