JIFFIES——系统滴答
CPU内部有一个RTC,会在上电的时候调用mktime函数算出从1970年1月1日0时开始到当前开机点所过的秒数
long kernel_mktime (struct tm *tm)
{long res;int year;year = tm->tm_year - 70; // 从70 年到现在经过的年数(2 位表示方式),// 因此会有2000 年问题。/* magic offsets (y+1) needed to get leapyears right. *//* 为了获得正确的闰年数,这里需要这样一个魔幻偏值(y+1) */res = YEAR * year + DAY * ((year + 1) / 4); // 这些年经过的秒数时间 + 每个闰年时多1 天res += month[tm->tm_mon]; // 的秒数时间,在加上当年到当月时的秒数。/* and (y+2) here. If it wasn't a leap-year, we have to adjust *//* 以及(y+2)。如果(y+2)不是闰年,那么我们就必须进行调整(减去一天的秒数时间)。 */if (tm->tm_mon > 1 && ((year + 2) % 4))res -= DAY;res += DAY * (tm->tm_mday - 1); // 再加上本月过去的天数的秒数时间。res += HOUR * tm->tm_hour; // 再加上当天过去的小时数的秒数时间。res += MINUTE * tm->tm_min; // 再加上1 小时内过去的分钟数的秒数时间。res += tm->tm_sec; // 再加上1 分钟内已过的秒数。return res; // 即等于从1970 年以来经过的秒数时间。
}
而给mktime函数传来的时间结构体的赋值是由初始化时从RTC(CMOS
)中读出的
// 该子程序取CMOS 时钟,并设置开机时间 startup_time(为从1970-1-1-0 时起到开机时的秒数)。
static void time_init(void)
{struct tm time;do {// 参见后面CMOS 内存列表。time.tm_sec = CMOS_READ(0);time.tm_min = CMOS_READ(2);time.tm_hour = CMOS_READ(4);time.tm_mday = CMOS_READ(7);time.tm_mon = CMOS_READ(8);time.tm_year = CMOS_READ(9);} while (time.tm_sec != CMOS_READ(0));BCD_TO_BIN(time.tm_sec);BCD_TO_BIN(time.tm_min);BCD_TO_BIN(time.tm_hour);BCD_TO_BIN(time.tm_mday);BCD_TO_BIN(time.tm_mon);BCD_TO_BIN(time.tm_year);time.tm_mon--;startup_time = kernel_mktime(&time);
}
可以看一下里面还有一个do while循环,是怕没有读到当前的值。
从main.c文件里进行的初始化。可以看到通过CMOS进行赋值的。这个就和硬件有关系了,咱也不懂,就当它能知道当前的时间吧。
所以这时就可以把时间存入全局变量中,会为JIFFIES所用
JIFFIES是一个系统的时钟滴答,一个系统滴答是10ms,也是一个定时器。
10ms一个系统滴答---->每隔10ms会引发一个定时器中断
而这个中断服务函数(timer_interrupt
)中,首先进行了jiffies的自加
_timer_interrupt:push ds ;// save ds,es and put kernel data spacepush es ;// into them. %fs is used by _system_callpush fspush edx ;// we save %eax,%ecx,%edx as gcc doesn'tpush ecx ;// save those across function calls. %ebxpush ebx ;// is saved as we use that in ret_sys_callpush eaxmov eax,10h ;// ds,es 置为指向内核数据段。mov ds,axmov es,axmov eax,17h ;// fs 置为指向局部数据段(出错程序的数据段)。mov fs,axinc dword ptr _jiffies //jiffies自加
;// 由于初始化中断控制芯片时没有采用自动EOI,所以这里需要发指令结束该硬件中断。mov al,20h ;// EOI to interrupt controller ;//1out 20h,al ;// 操作命令字OCW2 送0x20 端口。
;// 下面3 句从选择符中取出当前特权级别(0 或3)并压入堆栈,作为do_timer 的参数。mov eax,dword ptr [R_CS+esp]and eax,3 ;// %eax is CPL (0 or 3, 0=supervisor)push eax
;// do_timer(CPL)执行任务切换、计时等工作,在kernel/shched.c,305 行实现。call _do_timer ;// 'do_timer(long CPL)' does everything fromadd esp,4 ;// task switching to accounting ...jmp ret_from_sys_call
就是中断程序的正常流程,自加之后会调用一个do_timer函数
// 参数cpl 是当前特权级0 或3,0 表示内核代码在执行。
// 对于一个进程由于执行时间片用完时,则进行任务切换。并执行一个计时更新工作。
void do_timer (long cpl)
{extern int beepcount; // 扬声器发声时间滴答数(kernel/chr_drv/console.c,697)extern void sysbeepstop (void); // 关闭扬声器(kernel/chr_drv/console.c,691)// 如果发声计数次数到,则关闭发声。(向0x61 口发送命令,复位位0 和1。位0 控制8253// 计数器2 的工作,位1 控制扬声器)。if (beepcount)if (!--beepcount)sysbeepstop ();// 如果当前特权级(cpl)为0(最高,表示是内核程序在工作),则将超级用户运行时间stime 递增;// 如果cpl > 0,则表示是一般用户程序在工作,增加utime。if (cpl)current->utime++;elsecurrent->stime++;// 如果有用户的定时器存在,则将链表第1 个定时器的值减1。如果已等于0,则调用相应的处理
// 程序,并将该处理程序指针置为空。然后去掉该项定时器。if (next_timer){ // next_timer 是定时器链表的头指针(见270 行)。next_timer->jiffies--;while (next_timer && next_timer->jiffies <= 0){void (*fn) (); // 这里插入了一个函数指针定义!!!??fn = next_timer->fn;next_timer->fn = NULL;next_timer = next_timer->next;(fn) (); // 调用处理函数。}}
// 如果当前软盘控制器FDC 的数字输出寄存器中马达启动位有置位的,则执行软盘定时程序(245 行)。if (current_DOR & 0xf0)do_floppy_timer ();if ((--current->counter) > 0)return; // 如果进程运行时间还没完,则退出。current->counter = 0;if (!cpl)return; // 对于超级用户程序,不依赖counter 值进行调度。schedule ();
}
CPL变量就是内核中用来指示被中断程序的特权,也就是0表示内核进程,3表示用户进程。
current就是指向当前进程数据结构的指针,指向task_struct
所以utime就是用户程序的运行时间,stime是内核程序的运行时间
而这个next_timer就是一个时间链表的指针,也就是个定时器链表的指针
// 定时器链表结构和定时器数组。
static struct timer_list
{long jiffies; // 定时滴答数。void (*fn) (); // 定时处理程序。struct timer_list *next; // 下一个定时器。
}
timer_list[TIME_REQUESTS], *next_timer = NULL;
通过这个数据结构再看上面的程序代码,就可以知道,其实上面那段,就是在进行定时器中断的时候,会调用这个do_timer函数,而这个函数里面就会遍历判断这个定时器链表有没有当前定时器为0的,如果为0则证明到定的时间了,就会执行这个next_timer的fn,这个定时处理的函数。
而下面的current->counter
current上面提到了是执行进程的指针,而这个counter就是我们常听到的时间片。
如果时间片--后大于0,就直接返回,就是此进程还有时间片
CPU调度的,后面学了再写吧。
了解了这个系统滴答,和这个中断函数do_timer之后,就可以搞懂很多东西了,比如说异步的操作,可能是我太笨了,一开始对异步这种东西,完全没一点头绪,知道它能干什么,但是不知道它是怎么来了,后来再我看了点redis源码后,发现redis里面有一个时间事件time event这个东西,才明白异步就是通过定时器来完成的
而学习了这个linux内核的系统滴答后,才真正明白了这个操作是怎么完成的。
JIFFIES——系统滴答相关推荐
- 滴答定时器的计数模式_SysTick(系统滴答定时器)
一.SysTick概述 Systick定时器是一个24 位的倒计数定时器,计到0时,将从RELOAD 寄存器中自动重装载定时初值.只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息 ...
- STM32系统滴答定时器(systick)应用
一:系统滴答定时器(systick) 1.systick介绍 Systick就是一个定时器而已,只是它放在了NVIC中,主要的目的是为了给操作系统提供一个硬件上的中断(号称滴答中断).滴答中断?这里来 ...
- stm32系统滴答定时器使用
STM32菜鸟成长记录---系统滴答定时器(systick)应用 标签: delay任务测试reference编译器工作 2012-08-19 22:55 47395人阅读 评论(4) 收藏 举 ...
- stm32滴答计时器_stm32中的系统滴答定时器使用
系统滴答定时器对于stm32的初学者来说还是非常重要的,因为随着你学习的深入编程过程中肯定会调用延时函数,比如我之前的一些gpio相关的实验中.那么延时函数的编写也是几种方法的,一般开始接触都是让系统 ...
- STM32F10x_StdPeriph_Lib_V3.5.0库与系统滴答定时器(Systick)
在V3.5的库中,关于系统滴答的配置是在core_cm3.h文件中,其代码如下: static __INLINE uint32_t SysTick_Config(uint32_t ticks) { i ...
- STM32——系统滴答定时器
STM32--系统滴答定时器 宗旨:技术的学习是有限的,分享的精神是无限的. 一.SysTick[内核中] [风格:先描述一下库对寄存器的封装,再举例实现某些功能] SysTick定时器被捆绑在NVI ...
- 定时器开始时延时了十几秒_第六章--系统滴答定时器
第六章--系统滴答定时器 简介:系统滴答定时器是内核(这里指M4)定时器使用的是内核时钟源168MZ或可以选择外部时钟源21MZ 应用场合: 为UCOS系统提供时钟节拍 作为简单的定时器延时使用 // ...
- 系统滴答定时器的应用
1 实验目的 (1) 理解滴答定时器SysTick 定时器的工作原理: (2) 学会使用中断函数. 2 实验任务 (1) 编写 SysTick 定时器初始化程序: (2) 编写 SysTick 定时器 ...
- SysTick系统滴答定时器
工作原理 SysTick系统滴答定时器是一个24位递减计数器计数器,向下计数,最大计数值为() SysTick系统滴答定时器,每个时钟周期-1,减到0后申请中断,并且会自动重装初值. 注意: 定时器的 ...
最新文章
- [HTTP协议]入门篇
- 20个必会的JavaScript面试题
- Python代码实现飞机大战(经典)
- mysql 无论输入什么都是现实 not found_NotAPanda
- 收藏 | 小目标检测的一些问题,思路和方案
- Python迭代列表
- 小试ESP8266(一) 一只电阻, 几条语句, 摆脱深度睡眠反复重启的困扰
- hannoi塔(汉诺塔)移动过程解析
- 内事不决问张昭,外事不决问周瑜,“ 排序 ”不决问威少
- 如何在android中设置背景图片,在Android中设置窗口背景图
- MyHDL中文手册(十)——转换成Verilog和VHDL
- 算法笔记:找考试座位号问题
- 【动手学深度学习PyTorch版】6 权重衰退
- tcpdump+wireshark双剑合璧
- 使用FFmpeg 批量处理视频
- 计算机毕业设计论文网站综合比较
- 福娃Fuwa和海宝Haibao
- 面试腾讯软件测试经过1面2面3面,最终面试官对我竖起大拇指!
- 用python处理excel文件_python 读写 Excel文件
- vo,dto,po的区别