linux内核情景分析之强制性调度
从系统调用返回到用户空间是否调度,从ret_with_reschedule可看出,是否真正调度,取决于当前进程的pcb中的need_resched是否设置为1,那如何设置为1取决于以下几种情况:
void update_process_times(int user_tick){struct task_struct *p = current;int cpu = smp_processor_id(), system = user_tick ^ 1;update_one_process(p, user_tick, system, cpu);//统计信息而已if (p->pid) {if (--p->counter <= 0) {p->counter = 0;p->need_resched = 1;//强制调度}if (p->nice > 0)kstat.per_cpu_nice[cpu] += user_tick;elsekstat.per_cpu_user[cpu] += user_tick;kstat.per_cpu_system[cpu] += system;} else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)kstat.per_cpu_system[cpu] += system;}
如果此时发生系统调用,进入内核态,再发生中断,p->counter减为0,那么p->need_resched就置为1,中断返回后,然后系统调用返回时就会强制调度。
如果此时在用户态发生异常,进入内核态,再发生中断,p->counter减为0,那么p->need_resched就置为1,中断返回后,然后异常返回时就会强制调度。
/** Wake up a process. Put it on the run-queue if it's not* already there. The "current" process is always on the* run-queue (except when the actual re-schedule is in* progress), and as such you're allowed to do the simpler* "current->state = TASK_RUNNING" to mark yourself runnable* without the overhead of this.*/inline void wake_up_process(struct task_struct * p){unsigned long flags;/** We want the common case fall through straight, thus the goto.*/spin_lock_irqsave(&runqueue_lock, flags);p->state = TASK_RUNNING;//设置为可执行状态if (task_on_runqueue(p))//如果已经到run队列goto out;add_to_runqueue(p);//加入run队列reschedule_idle(p);//将唤醒进程与当前进程比较,如果唤醒进程比当前进程权值高,那就把当前进程的need_resched设置为1out:spin_unlock_irqrestore(&runqueue_lock, flags);}
static void reschedule_idle(struct task_struct * p){......int this_cpu = smp_processor_id();struct task_struct *tsk;tsk = cpu_curr(this_cpu);//获取当前进程的task_struct数据结构if (preemption_goodness(tsk, p, this_cpu) > 1)//比较当前进程和被唤醒的进程的综合权值tsk->need_resched = 1;//如果被唤醒的进程的综合权值比当前进程的大,那么强制调度}
对于第三种情况,实际上应被视为自愿的让出。但是,从内核代码的形式上看,也是通过相同的办法,将当前进程的need_resched标志置为1,使得在进程返回用户空间前夕发生调度,所以也放在这一节。此类系统调用有两个,一个是sched_setscheduler(),另一个是sched_yield()。
系统调用sched_setscheduler()的作用是改变进程的调度政策。用户登录到系统后,第一个进程的适用调度政策为SCHED_OTHER,也就是默认为无实时要求的交互式应用。在fork()创建新进程时则将此进程适用的调度政策遗传给了子进程。但是,用户可以通过系统调用sched_setscheduler()改变其适用调度政策。
sched_setscheduler,内核态对应的代码如下:
asmlinkage long sys_sched_setscheduler(pid_t pid, int policy,struct sched_param *param){return setscheduler(pid, policy, param);}asmlinkage long sys_sched_setparam(pid_t pid, struct sched_param *param){return setscheduler(pid, -1, param);}
static int setscheduler(pid_t pid, int policy,struct sched_param *param){struct sched_param lp;struct task_struct *p;int retval;retval = -EINVAL;if (!param || pid < 0)goto out_nounlock;retval = -EFAULT;if (copy_from_user(&lp, param, sizeof(struct sched_param)))//从用户空间把sched_param结构拷贝到lpgoto out_nounlock;/** We play safe to avoid deadlocks.*/read_lock_irq(&tasklist_lock);spin_lock(&runqueue_lock);p = find_process_by_pid(pid);//通过pid找到task_structretval = -ESRCH;if (!p)goto out_unlock;if (policy < 0)//policy为-1policy = p->policy;//维持原来的政策else {retval = -EINVAL;if (policy != SCHED_FIFO && policy != SCHED_RR &&policy != SCHED_OTHER)//必须是这三种政策之一goto out_unlock;}/** Valid priorities for SCHED_FIFO and SCHED_RR are 1..99, valid* priority for SCHED_OTHER is 0.*/retval = -EINVAL;if (lp.sched_priority < 0 || lp.sched_priority > 99)//实时进程的priority必须处于0-99goto out_unlock;if ((policy == SCHED_OTHER) != (lp.sched_priority == 0))//如果政策是SCHED_OTHER,sched_priority必须是0goto out_unlock;retval = -EPERM;if ((policy == SCHED_FIFO || policy == SCHED_RR) &&!capable(CAP_SYS_NICE))goto out_unlock;if ((current->euid != p->euid) && (current->euid != p->uid) &&!capable(CAP_SYS_NICE))goto out_unlock;retval = 0;p->policy = policy;p->rt_priority = lp.sched_priority;if (task_on_runqueue(p))move_first_runqueue(p);//从可执行进程队列的当前位置移到队列的前部,使其在调度时处于较为有利的地位current->need_resched = 1;//强制调度out_unlock:spin_unlock(&runqueue_lock);read_unlock_irq(&tasklist_lock);out_nounlock:return retval;}
asmlinkage long sys_sched_yield(void){/** Trick. sched_yield() first counts the number of truly* 'pending' runnable processes, then returns if it's* only the current processes. (This test does not have* to be atomic.) In threaded applications this optimization* gets triggered quite often.*/int nr_pending = nr_running;#if CONFIG_SMPint i;// Substract non-idle processes running on other CPUs.for (i = 0; i < smp_num_cpus; i++)if (aligned_data[i].schedule_data.curr != idle_task(i))nr_pending--;#else// on UP this process is on the runqueue as wellnr_pending--;#endifif (nr_pending) {//正在等待的运行的进程数/** This process can only be rescheduled by us,* so this is safe without any locking.*/if (current->policy == SCHED_OTHER)//当前进程调度策略为sched_othercurrent->policy |= SCHED_YIELD;//SCHED_YIELD标志位置1,在_schedule_tail清0current->need_resched = 1;//强制调度}return 0;}
linux内核情景分析之强制性调度的更多相关文章
- linux内核情景分析之execve()
用来描述用户态的cpu寄存器在内核栈中保存情况.可以获取用户空间的信息 struct pt_regs { long ebx; //可执行文件路径的指针(regs.ebx中 long ecx; //命令 ...
- Linux内核情景分析之消息队列
早期的Unix通信只有管道与信号,管道的缺点: 所载送的信息是无格式的字节流,不知道分界线在哪,也没通信规范,另外缺乏控制手段,比如保温优先级,管道机制的大小只有1页,管道很容易写满而读取没有及时,发 ...
- Linux内核情景分析的alloc_pages
NUMA结构的alloc_pages ==================== mm/numa.c 43 43 ==================== 43 #ifdef CONFIG_DISCON ...
- linux内核情景分析之exit与Wait
//第一层系统调用 asmlinkage long sys_exit(int error_code) { do_exit((error_code&0xff)<<8); } 其主体是 ...
- linux内核情景分析之内核中的互斥操作
信号量机制: struct sempahore是其结构,定义如下 struct semaphore { atomic_t count;//资源数目 int sleepers;//等待进程数目 wait ...
- Linux内核情景分析之异常访问,用户堆栈的扩展
情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果 ...
- linux内核情景分析之命名管道
管道是一种"无名","无形文件,只可以近亲进程使用,不可以再任意两个进程通信使用,所以只能实现"有名","有形"的文件来实现就可以 ...
- linux内核情景分析之信号实现
信号在进程间通信是异步的,每个进程的task_struct结构有一个sig指针,指向一个signal_struct结构 定义如下 struct signal_struct { atomic_t cou ...
- linux内核情景分析之匿名管道
管道的机制由pipe()创建,由pipe()所建立的管道两端都在同一进程.所以必须在fork的配合下,才可以在具有亲缘关系的进程通信 /* * sys_pipe() is the normal C c ...
随机推荐
- GBK UTF8 GB2132
GBK就是在保存你的帖子的时候,一个汉字占用两个字节,外国人看会出现乱码,为此我中华为自己汉字编码而形成之解决方案. UTF8就是在保存你的帖子的时候,一个汉字占用3个字节.但是外国人看的话不会乱码. ...
- Unity脚本执行顺序自研框架
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/52372611 作者:car ...
- 我给女朋友讲编程CSS系列(3) CSS如何设置字体的类型、大小、颜色,如何使用火狐浏览器的Firebug插件查看网页的字体
一.CSS如何设置字体的类型.大小.颜色 设计网页时,一般设置body的字体,让其他标签继承body的字体,这样设置特别方便,但是标题标签h1到h6和表单标签(input类型)是没有继承body的字体 ...
- html调用commonjs规范的js
a.js define(function(require, exports, module) { var test = function(){ console.log("hello worl ...
- 【Linked List Cycle】cpp
题目: Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ...
- pc和移动端获取滚动条的位置
移动端获取滚动条:document.body.scrollTop pc端获取滚动条:document.documentElement.scrollTop
- Memcached相关内容总结
1.Memcached常用命令总结 Memcached命令格式一般为: command 其中描述如下: 参数 描述 command 操作命令,一般为set/add/replace/get/delete ...
- docker常用命令整理
Docker常见用法整理 Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器. Docker 容器通过 Docker 镜像来创建.容器与镜像的关系类似于 ...
- 基于Jquery的商城商品图片的放大镜效果(非组件)
在开发商城的时候,往往会用到图片的放大功能,这里把自己在近期项目中使用的放大镜特效做一下总结(非插件). 放大镜效果 常用的js组件jquery.imagezoom,jquery.jqzoom,jqu ...
- Android获取系统时间yyyyMMddHHmmssSSS
代码改变世界 public String testTime1() throws ParseException { String DEFAULT_TIME_FORMAT = "yyyy-MM- ...