调度子系统1_调度子系统初始化

unsigned int sysctl_sched_rt_period = 1000000;
int sysctl_sched_rt_runtime = 950000;

//	参考:
//		SMP负载均衡
//		http://soft.chinabyte.com/os/22/12359522.shtml
//		linux组调度浅析
//		http://hi.baidu.com/_kouu/item/0fe32610e493314be75e06d1
//		进程调度和组调度
//		http://blog.chinaunix.net/uid-27052262-id-3239263.html
//		CF调度器
//		http://blog.csdn.net/wudongxu/article/details/8574749
//		FIFO调度器
//		http://lwn.net/Articles/296419/

//	调度初始化
//	函数任务:
//		1.初始化rootdomain
//			rootdomain指示rq可运行的cpu集合
//		2.初始化real-time task对cpu的占有率
//			sysctl_sched_rt_period代表rt进程的调度周期
//			sysctl_sched_rt_runtime代表rt进程在调度周期中可运行的时间
//		3.初始化per-cpu rq
//			3.1 初始化公平调度队列,实时调度队列
//			3.2 初始化cpu负载记录数组
//			3.3 初始化cpu使用的tick hrtimer
//		4.初始化current(init_task)为idle task
//			4.1 设置current由公平调度管理
1.1 void __init sched_init(void)
{
	int i, j;

#ifdef CONFIG_SMP
	//初始化默认的调度域
	init_defrootdomain();
#endif

	//rt_bandwidth表示实时进程对cpu的占有率
	init_rt_bandwidth(&def_rt_bandwidth,
			global_rt_period(), global_rt_runtime());

	//初始化per-cpu rq
	for_each_possible_cpu(i) {
		struct rq *rq;
		//per-cpu 运行队列
		rq = cpu_rq(i);
		raw_spin_lock_init(&rq->lock);
		rq->nr_running = 0;
		rq->calc_load_active = 0;
		rq->calc_load_update = jiffies + LOAD_FREQ;
		//初始化公平调度策略、实时调度策略队列
		init_cfs_rq(&rq->cfs, rq);
		init_rt_rq(&rq->rt, rq);
		//调度队列中,实时进程对cpu的占有率
		rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime;
		//分5个等级记录cpu的负载情况
		for (j = 0; j < CPU_LOAD_IDX_MAX; j++)
			rq->cpu_load[j] = 0;
#ifdef CONFIG_SMP
		rq->sd = NULL;
		rq->rd = NULL;
		rq->post_schedule = 0;
		rq->active_balance = 0;
		rq->next_balance = jiffies;
		rq->push_cpu = 0;
		//rq运行的cpu
		rq->cpu = i;
		rq->online = 0;
		rq->migration_thread = NULL;
		rq->idle_stamp = 0;
		rq->avg_idle = 2*sysctl_sched_migration_cost;
		INIT_LIST_HEAD(&rq->migration_queue);
		rq_attach_root(rq, &def_root_domain);
#endif
		//初始化rq使用的hrtimer
		init_rq_hrtick(rq);
		atomic_set(&rq->nr_iowait, 0);
	}

	set_load_weight(&init_task);

	//load balancing软中断
#ifdef CONFIG_SMP
	open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);
#endif

	atomic_inc(&init_mm.mm_count);
	//通知底层体系结构不需要切换虚拟地址空间的用户空间部分
	enter_lazy_tlb(&init_mm, current);

	//将当前进程,即init_task更新为idle thread
	init_idle(current, smp_processor_id());
	//下次进行load balancing的时间戳
	calc_load_update = jiffies + LOAD_FREQ;
	//当前进程关联的调度累
	current->sched_class = &fair_sched_class;

	perf_event_init();
	//标识调度器开始运行
	scheduler_running = 1;
}
//	rq结构分析
//	参考 http://blog.csdn.net/bullbat/article/details/7160246
1.2 struct rq {

	raw_spinlock_t lock;
	//就绪队列中进程数
	unsigned long nr_running;
	#define CPU_LOAD_IDX_MAX 5
	//分5个等级记录就绪队列的负载情况
	//在系统初始化的时候sched_init把rq的cpu_load array初始化为0,之后通过函数update_cpu_load
	//公式如下:
    //	cpu_load[0]等于rq中load.weight的值
    //	cpu_load[1]=(cpu_load[1]*(2-1)+cpu_load[0])/2
    //	cpu_load[2]=(cpu_load[2]*(4-1)+cpu_load[0])/4
    //	cpu_load[3]=(cpu_load[3]*(8-1)+cpu_load[0])/8
    //	cpu_load[4]=(cpu_load[4]*(16-1)+cpu_load[0]/16
    //通过this_cpu_load返回的cpu load值是cpu_load[0]
    //进行cpu blance或migration时,通过调用source_load target_load取得对该处理器cpu_load index值。
	unsigned long cpu_load[CPU_LOAD_IDX_MAX];
	//本schedule entity的load->weight的总和
	struct load_weight load;
	//scheduler tick中调用update_cpu_load时,这个值就增加一,可以用来反馈目前cpu load更新的次数。
	unsigned long nr_load_updates;
	//统计处理器context switch次数
	//	schedule进行累加,并可以通过函数nr_context_switches统计目前所有处理器总共的context switch次数
	//	或是可以查看/proc/stat中的ctxt获取目前整个系统触发context switch的次数。
	u64 nr_switches;
	//公平调度队列
	struct cfs_rq cfs;
	//实时调度队列
	struct rt_rq rt;

//支持group cfs tasks的机制
#ifdef CONFIG_FAIR_GROUP_SCHED
	//1.fair group scheduling,
    //	将cfs rq中若干task组织成若干task group,即子cfs_rq,属于这group的task所使用到的处理器时间就会以个group总共所分的的时间为上限。
    //2.基于cgroup的fair group scheduling 架构
    //	2.1 可以创造出有阶层性的task组织,根据不同task的功能群组化。
    //	2.2 在配置给该群组对应的处理器资源,让属于该群组下的task可以通过rq机制使用属于该群组下的资源。
    //3.加入、退出、遍历leaf_cfs_rq_list
    // 	3.1 加入:list_add_leaf_cfs_rq把一个group cfs rq加入。
    //	3.2 退出:list_del_leaf_cfs_rq把一个group cfs rq退出。
    //	3.3 遍历:for_each_leaf_cfs_rq遍历rq上得所有leaf cfs_rq
	struct list_head leaf_cfs_rq_list;
#endif

#ifdef CONFIG_RT_GROUP_SCHED
	//类似leaf_cfs_rq_list,只是这里是针对属于real-time的task,对应的操作函数有list_add_leaf_rt_rq,
	//list_del_leaf_rt_rq, for_each_leaf_rt_rq.
	struct list_head leaf_rt_rq_list;
#endif
	//统计目前rq中有多少task属于TASK_UNINTERRUPTIBLE的状态.
	unsigned long nr_uninterruptible;
	//指向目前处理器正在执行的task; 没有可运行task时,运行idle task.
	struct task_struct *curr, *idle;
	//基于处理器的jiffies值,用以记录下次进行处理器balancing的时间点.
	unsigned long next_balance;
	//用以存储context-switch发生时,前一个task的memory management结构.
	struct mm_struct *prev_mm;
	//记录目前rq的clock值
	//	该值等于调用sched_clock_cpu(cpu_of(rq))的返回值,在scheduler_tick中通过函数update_rq_clock更新.
	u64 clock;
	//记录目前rq中处于等待i/o状态的task数。
	//	例如当driver接受来自task的调用,但处于等待i/o阶段时,为了充分利用处理器的执行资源,
	//	这时就可以在driver中调用函数io_schedule,此时就会把目前rq中的nr_iowait加一,
    //	并设定目前task的io_wait为1, 然后触发scheduling 让其他task有机会可以得到处理器执行时间。
	atomic_t nr_iowait;

#ifdef CONFIG_SMP
	//root domain是基于多核心架构下的机制,
    //其中包括了:
    //	cpu mask(包括span,online,rt overload),
    //	reference count
    //	cpupri
    //1.当root domain被rq引用时,refcount加一,反之就减一。
    //2.cpu mask span表示rq可运行的cpu mask、 noline为已经被rq安排了进程的cpu
    //3.当rq中real-time的task执行完毕时,会调用函数pull_rt_task从该rq rto_mask中标识的cpu上,
    //	查找是否有处理器有大于一个以上的real-time task,若有就会迁移到本cpu执行。
    //4.cpupri不同于task的优先级,cpupri本身有102个优先级:
    //	-1 invalid,
    //	 0 idle
    //	 1 normal
    //	2-101对应real-time priority 0-99
    //参考convert_prio, task priority如果是140就会对应到cpu idle,如果是大于等于100就会对应到cpu normal,
    //	若是task priority介于0-99之间,就会对应到cpu real-time priority 101-2 之间。
    //在实际的操作上, 可以调用cpupri_find传入一个real-time task结构,此时就会依据cpupri中pri_to_cpu选择
    //	一个目前执行real-time task且该task的优先级比目前要插入的task更低的处理器, 并通过cpu mask(lowest_mask)
    //	返回目前可以选择的处理器mask。
    //参考 kernel/sched_cpupri.c.
    //在初始化的过程中,通过sched_init调用init_defrootdomain对root domain与cpu priority机制进行初始化。
	struct root_domain *rd;
	//schedule domain是基于多核心架构下的机制。
    //每个处理器都会有一个默认的scheduling domain.
    //	1.scheduling domain的层次结构:
    //		通过parent找到上一层的domain。
    //		通过child找到下一层的domain(NULL表示结尾)。
    //	2.scheduling domain覆盖的处理器:
    //		通过span掩码,表示这个domain所覆盖的处理器范围。
    //	3.scheduling domain的根:
    //		base domain涵盖系统中所有处理器。
    //	4.scheduling doma的cpu group:
    //		4.1 schedule domain都会包括一个或一个以上的cpu groups(结构为struct sched_group),并通过next指针把cpu groups串联在一起(成为一个单向的circular linked list)。
    //		4.2 cpu group都通过cpumask来定义其所涵盖的处理器,并且cpu group所包括的处理器范围必需涵盖在所属的schedule domain处理器范围中。
    //		4.3 当scheduling domain在balancing时,会以其下的cpu groups为单位,通过cpu_power(该group所涵盖的处理器的tasks loading
    //			总和)来比较不同的cpu groups的负荷来进行tasks的移动,达到balancing的目的。
    //	5.loadbalance时机:
    //		5.1 在sched_init中, 通过open_softirq注册SCHED_SOFTIRQ软中断\
    //		5.2 在scheduler_tick中,通过trigger_load_balance确认目前的jiffies值是否大于要触发load balance的时间戳,并通过raise_softirq触发SCHED_SOFTIRQ。
    //		5.3 在load balance软中断中,通过run_rebalance_domains进行scheduling domain load balance.
    //	有关scheduling domain进一步的內容,参考
    //	Documentation/scheduler/sched-domains.txt.
	struct sched_domain *sd;
	//为1表示目前cpu rq中执行的为idle task
	//为0表示执行非idle task
	unsigned char idle_at_tick;

	int post_schedule;

	//为1表示这个rq正在运行fair scheduling的load balance,此时会调用stop_one_cpu_nowait暂停该cpu的进程
    //然后通过调用active_load_balance_cpu_stop把tasks从最忙碌的处理器移到idle的处理器上执行
	int active_balance;
	//用以存储目前进入idle状态并且进行load balance流程的处理器id。
	//整个流程为
	//	进程调用schedule时, 若该处理器rq的nr_running为0(也就是目前没有正在执行的task),就会调用idle_balance并
	//	触发后续load balance流程
	int push_cpu;
	//用以存储目前运作这个rq的处理器id
	int cpu;
	int online;
	//如果rq中目前有task正在执行,这个值会等于目前该rq的load weight除以目前rq中task数目的均值
	unsigned long avg_load_per_task;

	struct task_struct *migration_thread;
	struct list_head migration_queue;
	//统计目前real-time task执行时间的均值, 反应目前系统中real-time task平均被分配到的执行时间值
	u64 rt_avg;
	u64 age_stamp;
	//表示cpu进入idle状态的时间
	u64 idle_stamp;
	u64 avg_idle;
#endif

	//用以记录下一次计算cpu load的时间,初始值为当前的jiffies加上五秒与1次的scheduling tick的间隔
	unsigned long calc_load_update;
	long calc_load_active;

#ifdef CONFIG_SCHED_HRTICK
#ifdef CONFIG_SMP

	int hrtick_csd_pending;
	struct call_single_data hrtick_csd;
#endif
	struct hrtimer hrtick_timer;
#endif

};
时间: 2024-11-02 09:36:49

调度子系统1_调度子系统初始化的相关文章

中断子系统1_中断子系统初始化

// 控制单元对中断信号的处理: // 当cpu执行一条指令后,cs和eip包含下一条要执行的指令的逻辑地址,在处理那条指令之前, // 控制单元会检查在运行前一条指令时是否发生了一个中断或异常,如果发生了一个中断或异常, // 控制单元执行下列操作: // 1.确定与中断或异常关联的向量i // 2.读入由idtr寄存器指向的IDT表中的第i项 // 3.从gdtr寄存器获得GDT的基地址,并在GDT中查找,以读取IDT表项中的选择符所标识的段描述符. // 这个描述符指定中断或异常处理程序的

调度器之单体调度器

本文讲的是调度器之单体调度器,[编者的话]本文描述了几个简单的单体调度器,基本的配置以及如何在Deis切换调度后端. 调度是一种向处理资源分配工作载荷的方式.在分布式环境中,调度器格外为大家需要,尤其是那些提供扩展性,资源意识以及高效能特性的调度器. 单体调度器是单个进程实体,进行调度决策并完成需要被调度的任务的部署.这些任务可以是长期运行的服务器程序,短期存在的批处理命令,MapReduce查询等等. 为了调度任务的决策,单体调度器需要:观察集群中资源的可用性(例如CPU.内存等),锁住资源,

网络子系统61_路由子系统初始化

// 路由缓存内存量指定办法: // 1.通过启动参数rhash_entries指定hash表bucket个数 // 2.根据物理内存页数确定使用的内存量 // 根据物理页数分配缓存内存: // 1.goal目标内存页数=总页数/(2**(26-PAGE_SHIFT)) // 2.最接近goal内存页数的order,用于从伙伴系统中分配 // 3.rt_hash_mask=order页可容纳的bucket数 // 4.对齐rt_hash_mask到2的幂次 // 5.从伙伴系统中分配order页

网络子系统15_arp邻居项初始化

// 初始化struct neighbour // 当邻居子系统新创一个neighbour时,邻居子系统调用特定协议的初始化函数,初始化邻居项. // 调用路径:neigh_create->arp_constructor // 函数主要任务: // 1.设置邻居项的地址类型,a,b,c,d // 2.使用与此邻居项关联的接口设备的neigh_param作为该邻居项调整邻居协议的参数. // 3.根据与此邻居项关联的接口设备的信息,初始化邻居项的状态,以及ops // 3.1 驱动没有提供填充l2

网络子系统27_桥接子系统初始化

//桥接子系统以模块的形式提供 //函数主要任务: // 1.转发数据库slab缓存 // 2.向socket的ioctl添加回调函数 // 3.在netif_receive_skb中路径上添加回调函数 // 4.向netdev_chain注册监听块 1.1 static int __init br_init(void) { //转发数据库初始化 br_fdb_init(); //桥接子系统中有关netfilter的初始化 ... //向socket的ioctl注册回调函数,处理对网桥的io命令

内存子系统1_分配接口

1.页 <linux/mm_types.h> struct page; 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理地址, 通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 内核struct page管理系统中所有的页. 2.区 <linux/mmzone.h> struct zone; Linux将内核空间地址划分为三个区:ZONE_DMA.ZONE_NORMAL和ZONE_HIGHMEM. 在x86结构中,三种类型的区域如下:

网络子系统14_邻居子系统通用接口

//创建一个新的邻居项 //参考 深入理解linux网络技术内幕 // 1.邻居子系统为具体的邻居协议,提供通用的功能接口 // 2.系统中所有的邻居协议被链接在neigh_tables链表中 // 3.neigh_table代表一个具体的邻居协议 // 4.具体邻居协议在运行时的行为,可以通过struct neigh_parms调节, // neigh_params与设备关联,每个邻居协议neigh_table提供一个默认的neigh_params. //注册一个邻居协议到系统中 // 1.与

网络子系统62_路由子系统处理设备事件

// 监听设备事件 // 在ip_rt_init->devinet_init中注册 1.1 static struct notifier_block ip_netdev_notifier = { .notifier_call =inetdev_event, }; // 路由子系统对网络设备事件的处理 // 与事件相关的设备需要有inet配置信息 // 函数主要任务: // 1.开启设备,加入多播组,为回环设备配置ip地址 // 2.关闭设备,设备退出多播组 // 3.设备注销,删除该设备的配置信

网络子系统63_路由子系统处理netlink事件

// 路由子系统netlink控制块 // 在ip_rt_init->devinet_init中注册. 1.1 static struct rtnetlink_link inet_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = { [4] = { .doit = inet_rtm_newaddr, }, [5] = { .doit = inet_rtm_deladdr, }, [6] = { .dumpit = inet_dump_ifaddr, }, [8]