时间子系统2_tick device管理机制

//	1.每个cpu都具有一个tick_device,维护周期时钟。
//	2.tick_device依赖一个clockevent设备,提供周期事件。
//	3.cpu电源状态的改变会影响tick_device,通过tick_notifier监听电源状态。
//	4.全局广播设备接管进入省电模式cpu的周期时间维护。
//	4.broadcast_mask保存开启广播模式的cpu, broadcast_oneshot_mask保存进入省电模式的cpu。

//	监听clockevent设备状态
//	调用路径:start_kernel->tick_init
1.1 void __init tick_init(void)
{
	clockevents_register_notifier(&tick_notifier);
}

//	监听clockevent设备,cpu的状态变化
1.2 static int tick_notify(struct notifier_block *nb, unsigned long reason,
			       void *dev)
{
	switch (reason) {

		//新eventclock设备注册
	case CLOCK_EVT_NOTIFY_ADD:
		return tick_check_new_device(dev);
		//cpu开启\关闭广播模式
	case CLOCK_EVT_NOTIFY_BROADCAST_ON:
	case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
	case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
		tick_broadcast_on_off(reason, dev);
		break;
		//cpu进入\离开广播模式
	case CLOCK_EVT_NOTIFY_BROADCAST_ENTER:
	case CLOCK_EVT_NOTIFY_BROADCAST_EXIT:
		tick_broadcast_oneshot_control(reason);
		break;
		//选择do_timer的cpu
	case CLOCK_EVT_NOTIFY_CPU_DYING:
		tick_handover_do_timer(dev);
		break;
		//停用dead cpu的clockevent
	case CLOCK_EVT_NOTIFY_CPU_DEAD:
		tick_shutdown_broadcast_oneshot(dev);
		tick_shutdown_broadcast(dev);
		tick_shutdown(dev);
		break;
		//挂起cpu的clockevent
	case CLOCK_EVT_NOTIFY_SUSPEND:
		tick_suspend();
		tick_suspend_broadcast();
		break;
		//恢复cpu的clockevent
	case CLOCK_EVT_NOTIFY_RESUME:
		tick_resume();
		break;

	default:
		break;
	}

	return NOTIFY_OK;
}

//	向clockevents_chain通知clockevents事件
//	函数主要任务:
//		1.通知clockevents_chain
//		2.当cpu offline时,清空clockevents_released链表,删除与此cpu相关的clockevent
//	注:clockevents_released保存所有fail-to-add/replace-out的clockevent
//		clockevent_devices保存所有有效的clockevent
1.3 void clockevents_notify(unsigned long reason, void *arg)
{
	struct clock_event_device *dev, *tmp;
	unsigned long flags;
	int cpu;

	raw_spin_lock_irqsave(&clockevents_lock, flags);
	//封装标准通知链操作raw_notifier_call_chain
	clockevents_do_notify(reason, arg);

	switch (reason) {
	case CLOCK_EVT_NOTIFY_CPU_DEAD:
		//clockevents_released用于保存所有fail-to-add/replace-out的clockevent
		//当cpu offline后,清空此链表
		list_for_each_entry_safe(dev, tmp, &clockevents_released, list)
			list_del(&dev->list);
		//删除与此cpu相关的clockevent
		cpu = *((int *)arg);
		list_for_each_entry_safe(dev, tmp, &clockevent_devices, list) {
			if (cpumask_test_cpu(cpu, dev->cpumask) &&
			    cpumask_weight(dev->cpumask) == 1 &&
			    !tick_is_broadcast_device(dev)) {
				BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
				list_del(&dev->list);
			}
		}
		break;
	default:
		break;
	}
	raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}

//	cpu开启\关闭广播模式
//	调用路径:tick_notify->tick_broadcast_on_off
//	注:
//		1.加入、退出广播组指针对online的cpu
2.1 void tick_broadcast_on_off(unsigned long reason, int *oncpu)
{
	//忽略offline的cpu
	if (!cpumask_test_cpu(*oncpu, cpu_online_mask))
		printk(KERN_ERR "tick-broadcast: ignoring broadcast for "
		       "offline CPU #%d\n", *oncpu);
	else
		tick_do_broadcast_on_off(&reason);

}
//	cpu开启\关闭广播模式
//	函数主要任务:
//		1.开启广播模式
//			1.1 将cpu加入广播组bitmap中
//			1.2 如果广播设备为周期触发模式,关闭此clockevent
//		2.关闭广播模式
//			2.1 从广播组bitmap中删除cpu
//			2.2 如果广播设备为周期触发模式,恢复其clockevent
//		3.如果广播组为空
//			3.1 停止全局广播设备
//		4.否则
//			4.1 设置全局广播设备为单触发模式或周期模式
//	注:
//		1.全局广播设备tick_broadcast_device
//		2.每个cpu有各自的tick_device,选择其中一个充当全局广播设备
//		3.加入广播组的clockevent需要关闭其周期触发模式
2.2 static void tick_do_broadcast_on_off(unsigned long *reason)
{
	struct clock_event_device *bc, *dev;
	struct tick_device *td;
	unsigned long flags;
	int cpu, bc_stopped;

	raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
	cpu = smp_processor_id();
	//per cpu的tick device
	td = &per_cpu(tick_cpu_device, cpu);
	dev = td->evtdev;
	//全局广播设备
	bc = tick_broadcast_device.evtdev;

	//设备不受电源状态的影响
	if (!dev || !(dev->features & CLOCK_EVT_FEAT_C3STOP))
		goto out;

	//广播组是否为空
	bc_stopped = cpumask_empty(tick_get_broadcast_mask());
	switch (*reason) {
	//开启cpu的广播模式
	case CLOCK_EVT_NOTIFY_BROADCAST_ON:
	case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
		//当前cpu不在广播组中
		if (!cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
			//将cpu加入广播组
			cpumask_set_cpu(cpu, tick_get_broadcast_mask());
			//关闭周期触发模式的clockevent设备
			if (tick_broadcast_device.mode ==
			    TICKDEV_MODE_PERIODIC)
				clockevents_shutdown(dev);
		}
		break;
	//关闭cpu的广播模式
	case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
		if (!tick_broadcast_force &&
			//从广播组中清除该设备
		    cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
			cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
			//恢复该设备的周期触发模式
			if (tick_broadcast_device.mode ==
			    TICKDEV_MODE_PERIODIC)
				tick_setup_periodic(dev, 0);
		}
		break;
	}
	//删除了广播组中最后一个设备
	if (cpumask_empty(tick_get_broadcast_mask())) {
		if (!bc_stopped)
		{	//停止全局广播设备
			clockevents_shutdown(bc);
		}
	} else if (bc_stopped) {
		if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
			tick_broadcast_start_periodic(bc);
		else
			tick_broadcast_setup_oneshot(bc);
	}
out:
	raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}

//	cpu进入\离开广播模式
//		当cpu进入省电模式时,由全局广播设备接管cpu的时间维护
//	调用路径:tick_notify->tick_broadcast_oneshot_control
//	函数主要任务:
//		1.cpu进入省电模式,由全局广播设备接管其时间维护
//			1.1 设置cpu关闭状态,更新全局广播设备的到期时间
//		2.cpu离开省电模式,全局广播设备不再接管其时间维护
//			2.1 更新全局广播设备的到期时间
//	注:
//		cpu在进入省电模式时,进入广播模式
//		cpu在离开省电模式时,退出广播模式
3.1 void tick_broadcast_oneshot_control(unsigned long reason)
{
	struct clock_event_device *bc, *dev;
	struct tick_device *td;
	unsigned long flags;
	int cpu;

	raw_spin_lock_irqsave(&tick_broadcast_lock, flags);

	//电源状态变化不影响周期模式的全局广播设备
	if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
		goto out;

	bc = tick_broadcast_device.evtdev;
	cpu = smp_processor_id();
	td = &per_cpu(tick_cpu_device, cpu);
	dev = td->evtdev;

	//cpu的clockevent设备不受电源状态的影响
	if (!(dev->features & CLOCK_EVT_FEAT_C3STOP))
		goto out;

	//cpu进入省电模式,由全局广播设备接管其tick device的任务
	if (reason == CLOCK_EVT_NOTIFY_BROADCAST_ENTER) {
		//设置cpu关闭状态
		if (!cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
			cpumask_set_cpu(cpu, tick_get_broadcast_oneshot_mask());
			clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN);
			//cpu下一个事件的到期时间小于全局广播设备下一个事件的到期时间,设置cpu在下一个周期到期
			if (dev->next_event.tv64 < bc->next_event.tv64)
				tick_broadcast_set_event(dev->next_event, 1);
		}
	//cpu离开省电模式,全局广播设备不再接管其tick device的任务
	} else {
		//清除cpu关闭状态
		if (cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
			cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
			clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
			//重新编程cpu的tick device在下一个周期到期
			if (dev->next_event.tv64 != KTIME_MAX)
				tick_program_event(dev->next_event, 1);
		}
	}

out:
	raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}

//	选择do_timer cpu
//	调用路径:tick_notify->tick_handover_do_timer
4.1 static void tick_handover_do_timer(int *cpup)
{
	//如果dying cpu负责do_timer,重新选取online cpu中第一个cpu负责
	if (*cpup == tick_do_timer_cpu) {
		int cpu = cpumask_first(cpu_online_mask);

		tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
			TICK_DO_TIMER_NONE;
	}
}

//	dead cpu的clockevent的处理:
//		1.从oneshot掩码中删除cpu
//		2.从broadcast掩码中删除cpu
//		3.关闭clockevent设备
//			3.1 设置该cpu tick device的clockevent为null
//			3.2 将clockevent设备链入clockevents_released

//	从oneshot掩码中删除cpu
//	调用路径:tick_notify->tick_shutdown_broadcast_oneshot
5.1 void tick_shutdown_broadcast_oneshot(unsigned int *cpup)
{
	unsigned long flags;
	unsigned int cpu = *cpup;

	raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
	//从oneshot掩码中删除dead cpu
	cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
	raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}

//	从broadcast掩码中删除cpu
//	调用路径:tick_notify->tick_shutdown_broadcast
5.2 void tick_shutdown_broadcast(unsigned int *cpup)
{
	struct clock_event_device *bc;
	unsigned long flags;
	unsigned int cpu = *cpup;

	raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
	//从broadcast掩码中删除dead cpu
	bc = tick_broadcast_device.evtdev;
	cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
	//广播组为空,停止全局广播广播设备
	if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) {
		if (bc && cpumask_empty(tick_get_broadcast_mask()))
			clockevents_shutdown(bc);
	}

	raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}

//	关闭cpu的clockevent设备
//	调用路径:tick_notify->tick_shutdown
5.3 static void tick_shutdown(unsigned int *cpup)
{
	struct tick_device *td = &per_cpu(tick_cpu_device, *cpup);
	struct clock_event_device *dev = td->evtdev;
	unsigned long flags;

	raw_spin_lock_irqsave(&tick_device_lock, flags);
	td->mode = TICKDEV_MODE_PERIODIC;
	//标记给定cpu的clockevent设备为不可用
	if (dev) {
		dev->mode = CLOCK_EVT_MODE_UNUSED;
		//将clockevent链接到clockevents_released
		clockevents_exchange_device(dev, NULL);
		td->evtdev = NULL;
	}
	raw_spin_unlock_irqrestore(&tick_device_lock, flags);
}

//	挂起cpu的clockevent
//	调用路径:tick_notify->tick_suspend
6.1 static void tick_suspend(void)
{
	struct tick_device *td = &__get_cpu_var(tick_cpu_device);
	unsigned long flags;
	//关闭cpu tick device的clockevent
	raw_spin_lock_irqsave(&tick_device_lock, flags);
	clockevents_shutdown(td->evtdev);
	raw_spin_unlock_irqrestore(&tick_device_lock, flags);
}

//	恢复cpu的clockevent
//	调用路径:tick_notify->tick_resume
7.1 static void tick_resume(void)
{
	struct tick_device *td = &__get_cpu_var(tick_cpu_device);
	unsigned long flags;
	//恢复广播
	int broadcast = tick_resume_broadcast();

	raw_spin_lock_irqsave(&tick_device_lock, flags);
	clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_RESUME);
	//恢复clockevent的触发模式
	if (!broadcast) {
		if (td->mode == TICKDEV_MODE_PERIODIC)
			tick_setup_periodic(td->evtdev, 0);
		else
			tick_resume_oneshot();
	}
	raw_spin_unlock_irqrestore(&tick_device_lock, flags);
}
时间: 2024-12-26 10:41:00

时间子系统2_tick device管理机制的相关文章

时间子系统12_clockevent设备注册

// 向系统注册时钟事件设备 // 函数任务: // 1.添加设备到clockevent_devices链表 // 2.通知tick device管理机制有clockevent设备注册 1.1 void clockevents_register_device(struct clock_event_device *dev) { unsigned long flags; raw_spin_lock_irqsave(&clockevents_lock, flags); //将设备添加到clockeve

Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)【转】

转自:http://blog.csdn.net/droidphone/article/details/8112948 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 数据结构 低分辨率下的动态时钟 1  切换至动态时钟模式 2  低分辨率动态时钟下的事件中断处理函数 3  动态时钟停止周期tick时钟事件 3  动态时钟重新开启周期tick时钟事件 高精度模式下的动态时钟 动态时钟对中断的影响   在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由

深入理解和改进JSP/Servlet会话管理机制

js|servlet 在Web服务器端编程中,会话状态管理是一个经常必须考虑的重要问题.本文分析JSP/Servlet的会话管理机制及其所面临的问题,然后提出了一种改进的会话管理方法. 一.Servlet的会话管理机制 根据设计,HTTP是一种无状态的协议.它意味着Web应用并不了解有关同一用户以前请求的信息.维持会话状态信息的方法之一是使用Servlet或者JSP容器提供的会话跟踪功能.Servlet API规范定义了一个简单的HttpSession接口,通过它我们可以方便地实现会话跟踪. H

Slackware Linux技术内幕之包管理机制

郑重声明:本文从行文构思到章节安排均由本人独自完成,未参考任何网文,书籍.杂志.没有任何侵权行为.文中内容如果是参考网文的,我均已做明确标示. 简介:包管理机制是Slackware区别于其他Linux 发行版的重要的甚至是本质的区别.Slackware的包管理机制简单.灵活.高效.安全,用户透明性大,可定制程度高.因为Slackware的标准包管理工具皆为Shell脚本,这样,你可用简单而又方便的对其进行修改.移植.但是也存在一些不足之处,比如没有灵活的网络安装模式等.本文对 Slackware

Tomcat的Session管理机制

Session和Cookie请求的过程 Http连接本身是无状态的,即前一次发起的连接跟后一次没有任何关系,是属于两次独立的连接请求, 但是互联网访问基本上都是需要有状态的,即服务器需要知道两次连接请求是不是同一个人访问的. JSESSIONID是一个唯一标识号,用来标识服务器端的Session,也用来标识客户端的Cookie,客户端和服务器端通过这个JSESSIONID来一一对应. 客户端第一次请求到服务器连接,这个连接是没有附带任何东西的,没有Cookie,没有JSESSIONID. 服务器

Http的状态管理机制(cookie)

HTTP状态管理机制 摘要 这篇文档是为HTTP request 和 response之间创建一个有状态的会话指明一个方法,并描述了两个头字段:Cookie和Set-cookie,用于携带服务端和客户端之间的状态信息. 术语 FQHN(fully-qualified host name):指的是主机的FQDN(fully-qualified domain name),比如以1级域名.com 或 .uk结尾的完全指定的域名,或者指主机的IP地址.(倾向于前者,后者不建议) request-host

Tomcat中session的管理机制_java

详细描述Tomcat中session的管理机制: 1. 请求过程中的session操作: 简述:在请求过程中首先要解析请求中的sessionId信息,然后将sessionId存储到request的参数列表中.然后再从 request获取session的时候,如果存在sessionId那么就根据Id从session池中获取session,如果sessionId不 存在或者session失效,那么则新建session并且将session信息放入session池,供下次使用. (1)SessionId

深入SQL SERVER 2000的内存管理机制(二)

server 深入SQL SERVER 2000的内存管理机制(二)     http://msdn.microsoft.com/data/default.aspx?pull=/library/en-us/dnsqldev/html/sqldev_01262004.asp   可访问大地址的应用 (Large-Address-Aware Executables) 在Windows增加支持/3GB参数以前,一个应用程序是无法访问一个带有高位设置的指针.一个32位的指针只有前31位地址空间可以被用户

深入理解计算机系统-之-内存寻址(三)--分段管理机制(段描述符,段选择子,描述符表)

逻辑地址-=>线性地址-=>物理地址 前面我们提到了当使用80x86微处理器时,有三种不同的地址: 逻辑地址(logical address):包含在机器语言指令中用来指定一个操作数或一条指令的地址.这个寻址方式在80x86著名的分段结构中表现得尤为具体,它促使MS-DOS或Windows程序员把程序分成若干段.每一个逻辑地址都由一个段(segment)和偏移量(offset或displacement)组成,偏移量指明了从段开始的地方到实际地址之间的距离. 线性地址(linear addres